1
0
mirror of https://github.com/Maaaac/408.git synced 2026-02-03 10:33:16 +08:00
Files
408kaoshi/09
2021-12-20 19:45:17 +08:00

487 lines
17 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//2009、含头指针的单链表查找其链表中倒数第K个位置上的结点
typedef int ElemType; //链表数据的结构定义
typedef struct LNode{ //链表结点的结构定义
ElemType data; //结点数据
struct Lnode *link; //结点链接指针
}*LinkList;
int Search_k(LinkList list ,int k){
Linklist p = list->link , q = list->link; //指针p、q指向第一个结点
int count = 0;
while(p!= NULL){ //遍历链表直到最后一个结点
if(count < k){
count ++;
p = p->link;
}
else{
p = p->link;
q = q->link;
}
}
if (count < k){ //结点数不足K
return 0;
}
else{
cout<<q->data;
return 1;
}
}
---------------------------------------------------------------------------------------------------------------------------------------
//2010、含n个整数的一维数组R将R中保存的序列循环左移p个位置即将R( a0,a1,....,a(n-1) )变换为R( ap,a(p+1),....a(n-1),a0,a1,....,a(p-1) )
//思路同:将字符串分开的两个子串相互倒转
void Reverse(int R[],int from,int to){
int i,temp;
for(int i = 0; i<(to-from+1)/2;i++){
temp = R[from+i];
R[from+i] = R[to-i];
R[to-i] = temp;
}
}
void Converse(int R[],int n,int p){
Reverse(R,0,p-1);
Reverse(R,p,n-1);
Reverse(R,0,n-1);
}
---------------------------------------------------------------------------------------------------------------------------------------
//2011、对于长度为L的升序序列S处在第L/2个位置的数成为S的中位数求两个等长升序序列A、B的中位数
/*思路:
1、先分别求出A、B的中位数a、b
2、若a=b则a或b为所求
3、若a<b,则舍弃A中较小的一半和B中较大一半(舍弃长度相等)
4、若a>b,则舍弃A中较大一半和B中较小一半(舍弃长度相等)
5、重复1234步直到最后都只剩一个元素较小者为所求
*/
int M_Search(int A[],int B[],int n){
int s1=0,d1=n-1,m1,s2=1,d2=n-1,m2; //分别表示序列A和B的首位数、末位数、中位数
while(s1 != d1 || s2 != d2 ){
m1 = (s1+d1)/2;
m2 = (s2+d2)/2;
if(A[m1]==B[m2]){
return A[m1]; //满足条件a=b
}
if(A[m1]<B[m2]){ //a<b
if((s1+d1)%2==0){
s1=m1;
d2=m2;
}
else{
s1=m1+1;
d2=m2;
}
}
else{
if((s1+d1)%2==0){
d1=m1;
s2=m2;
}
else{
d1=m1;
s2=m2+1;
}
}
return A[s1]<B[S2]? A[s1]:[s2];
}
---------------------------------------------------------------------------------------------------------------------------------------
/*2012
用带头结点的单链表保存单词当两个单词有相同的后缀时可共享相同的后缀存储空间例如“loading”和“being”
设str1和str2分别指向两个单词所在的单链表的头结点找出两个链表共同后缀的起始位置
*/
/*思路:找到共同后缀的第一个结点
1、分别求出str1和str2所指两个链表的长度m、n
2、两个链表以表尾对齐另指针p、q分别指向str1和str2的头结点若m>=n则使p指向链表中的第m-n+1个结点反之则使q指向其第m-n+1个结点
3、反复将p和q同步向后移动并判断它们是否指向同一结点。
*/
typedef char ElemType;
typedef struct LNode
{
ElemType data;
struct Lnode *link
}LinkList;
LinkNode *Find_1st_common(LinkList str1,LinkList str2){
int len1=length(str1),len2=length(str2);
LinkNode *p,*q;
for(p=str1;len1>len2;len1--){
p=p->next;
}
for(q=str2;len1<len2;len2--){
q=q->next;
}
while(p->next!=NULL&&p->next!=q->next){
p=p->next;
q=q->next;
}
return p->next;
}
---------------------------------------------------------------------------------------------------------------------------------------
//2013、整数序列A(a0,a1,...,a(n-1) ),其中0<a<n 若存在值相等的元素x且个数m>n/2则称x为A的主元素设计算法求A的主元素
/*思路从前往后扫描数组元素标记出一个可能成为主元素的元素Num然后重新计数确认Num是否是主元素
1、选取候选的主元素依次扫描所给数组中的每个证书将第一个遇到的证整数Num保存到c中记录Num的出现次数为1
若遇到的下一个整数仍等于Num则计数+1否则计数减1
当计数减到0时将遇到的下一个整数保存到c中计数重新记为1开始新一轮计数即从当前位置开始重复上述扫完全部元素
2、判断c中元素是否是真正的主元素再次扫描该数组统计c中元素出现的次数若大于n/2则为主元素
*/
int Majority(int A[],int n){
int i,c,count=1;
c = A[0];
for(i=1;i<n;i++){
if(A[i] ==c){
count++;
}
else if(count>0){
count--;
}
else{
c = A[i];
count = 1;
}
}
if(count>0){
for(i=count=0;i<n;i++){
if(A[i] == c){
count++;
}
}
if(count>=n/2){
return c;
}
else{
return -1;
}
}
}
---------------------------------------------------------------------------------------------------------------------------------------
/*2014
二叉树的带权路径长度(WPL)是二叉树中所有叶结点的带权路径长度之和。
给定一棵二叉树采用二叉链表存储其中叶结点weight域保存该结点的非负权值left指向其左孩子right指向其右孩子
设root为指向T的根结点设计求T的WPL的算法
*/
//二叉树结点的数据类型定义
typedef struct BiTNode{
int weight;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
/*思路:
方法1(先序遍历递归算法)用static变量记录WPL
1、若该结点是叶子结点那么变量wpl加上该结点的深度与权值之积
2、若该结点非叶子结点那么若左子树不空对左子树调用递归算法若右子树不空对右子树调用递归算法深度参数均为本结点深度参数+1
3、最后返回计算出的wpl即可
*/
int WPL(BiTree root){
int wpl_PreOrder(root,0);
}
int wpl_PreOrder(BiTree root,int deep){
static int wpl = 0; //定义一个static变量存储wpl
if(root->lchild == NULL && root->rchild == NULL){
wpl += deep * root -> weight ; //若为叶子结点累计wpl
}
if(root->lchild != NULL){
wpl_PreOrder(root->lchid,deep+1);
}
if(root->rchild !=NULL){
wpl_PreOrder(root->rchild,deep+1);
}
return wpl;
}
/*
方法2(基于层次遍历的队列)
1、用队列进行层次遍历记录当前的层数当遍历到叶子结点时累计wpl
2、当遍历到非叶子结点时把该结点的子树加入队列
3、当某结点为该层的最后一个结点时层数自增1
4、队列空时遍历结束返回wpl
*/
#define MaxSize 100 //设置队列的最大容量
int wpl_LevelOrder(BiTree root){
BiTree q[MaxSize]; //声明队列,end1为头指针end2为尾指针
int end1,end2; //队列最多容纳MaxSize-1个元素
end1 = end2 =0 ; //头指针指向队头元素,尾指针指向队尾的后一个元素
int wpl = 0,deep =0; //初始化wpl和深度
BiTree lastNode; //lastNode用来记录当前层的最后一个结点
BiTree newlastNode; //newlastNode用来记录下一层的最后一个结点
lastNode = root; //lastNode初始化为根结点
newlastNode = NULL; //newlastNode初始化为空
q[end2++] = root; //根结点入队
while(end1 != end2){ //层次遍历,若队列不空则循环
BiTree t = q[end1++]; //拿出队列中的头一个元素
if(t->lchild == NULL && t->rchild == NULL){
wpl += deep*t->weight; //若为叶子结点统计wpl
}
if(root->lchild != NULL){ //若非叶子结点把左结点入队
q[end2++] = t->lchild;
newlastNode = t->lchild;
}
if(root->rchild != NULL){
q[end2++] = t->rchild;
newlastNode = t->rchild;
}
if(t == lastNode){
lastNode = newlastNode;
deep += 1;
}
}
return wpl
}
---------------------------------------------------------------------------------------------------------------------------------------
/*2015
用单链表保存m个整数结点的结构为[data][link],且[data]<=n
设计一个时间复杂度尽可能高效的算法对于链表中data的绝对值相等的结点仅保留第一次出现的结点而删除其余绝对值相等的结点
如链表21 -> -15 -> -15 -> -7 -> 15,删除结点后为21 -> -15 -> -7
*/
/*
思路:因|data|<=n,故辅助数组q的大小为n+1各元素的初值均为0.
依次扫描链表中各结点同时检查q[|data|]的值若为0则保留结点并令q[|data|]=1否则将该结点删除
*/
//单链表结点的数据类型定义
typedef struct node{
int data;
struct node *link;
}NODE;
typedef NODE *PNODE
//算法实现
void func(PNODE h,int n){
PNODE p = h , r;
int *q,m;
q = (int *)malloc(sizeof(int)*(n+1)); //申请n+1个位置的辅助空间
for(int i=0;i<n+1;i++){
*(q+i) = 0;
}
while(p->link != NULL){
m = p->link->data >0 ? p->link->data:-p->link->data;
if(*(q+m) == 0){ //判断该结点的data是否已出现过
*(q+m) =1;
p = p->link;
}
else{
r = p->link;
p->link = r->link;
free(r);
}
}
free(q);
}
---------------------------------------------------------------------------------------------------------------------------------------
/*2016
已知由n(n>=2)个正整数构成的集合A将其划分为两个不相交的子集A1和A2元素个数分别为n1和n2
令A1和A2中元素之和为S1和S2设计划分算法满足|n1-n2|最小且|S1-S2|最大
*/
/*思路:
将最小的n/2(向下取整,下同)个元素放在A1中其余的元素放在A2中分组结果即可满足题意
利用快速排序的思想基于枢轴将n个整数划分为两个子集根据划分后枢轴所处的位置i分别处理
1、若i=n/2,则分组完成,算法结束
2、若i<n/2,则枢轴及之前的所有元素均属于A1继续对i之后的元素进行划分
3、若i>n/2,则枢轴及之后的所有元素均属于A2继续对i之前的元素进行划分
*/
int setPartition(int a[],int n[]){
int pivotkey,low=0,low0=0,high=n-1,flag=1,k=n/2,i;
int s1=0,s2=0;
while(flag){
pivotkey=a[low];
while(low<high && a[high]>=pivotkey){
--high;
}
if(low != high){
a[low]=a[high];
}
while(low<high && a[low]<=pivotkey){
++low;
}
if(low != high){
a[high]=a[low];
}
a[low] = pivotkey;
if(low == k-1){
flag = 0;
}
else{
if(low < k-1){
low0 = ++low;
high = high0;
}
else{
high0 = --high;
low = low0;
}
}
}
for(i=o;i<k;i++){
s1+=a[i];
}
for(i=k;i<n;i++){
s2+=a[i];
}
return s2-s1;
}
---------------------------------------------------------------------------------------------------------------------------------------
/*2017
设计算法,将给定的表达式树(二叉树)转换为等价的中缀表达式(通过括号反映操作符的计算次序)并输出
如下列两棵表达树作为算法的输入时,输出的等价中缀表达式分别为(a+b)*(c*(-d))和(a*b)+(-(c-d))
* +
/ \ / \
+ * * -
/ \ / \ / \ \
a b c - a b -
\ / \
d c d
*/
/*思路:
表达树的中序序列加上必要的括号即为等价的中缀表达式
基于二叉树的中序遍历策略
*/
//二叉树结点定义:
typedef struct node{
char data[10]; //存储操作数或操作符
struct node *left,*right;
}BTree;
//算法
void BtreeToE(BTree *root){
BtreeToExp(root,1); //根的高度为1
}
void BtreeToExp(BTree *root,int deep){
if(root == NULL){return;}
else if(root->left == NULL && root->right == NULL){
printf("%s",root->data );
}
else{
if(deep>1){
printf("("); //若有子表达式则加一层括号
}
BtreeToExp(root->left,deep+1);
printf("%s",root->data );
BtreeToExp(root->right,deep+1);
if(deep>1){
printf(")"); //若由子表达式则加一层括号(此处是为了封闭次结点层的运算)
}
}
}
---------------------------------------------------------------------------------------------------------------------------------------
/*2018
给定一个含n个整数的数组设计一个时间上尽可能高效的算法找出数组中未出现的最小正整数
例如,数组{-5,3,2,3}中未出现的最小正整数是1数组{1,2,3}中未出现的最小正整数是4
*/
/*思路:
由于A中只有n个数因此若最小的n个正整数全在A内的话未出现的最小正整数则为n+1反之将A中n个数扫一遍后1~n中有数没被扫到则最小的那个为所求
分配一个用于标记的数组B[n],用来记录A中是否出现了1~n中的正整数B[0]对应正整数1B[n-1]对应正整数n初始化B中所有值为0
从A[0]开始遍历A若0<A[i]<=n,则令B[A[i]-1]==1,否则不做任何操作(对负数和大于n的数都不做处理)
对A遍历结束后开始遍历数组B找到第一个满足B[i]==0的下标若找到返回结果(i+1),否则返回(n+1)
*/
int findMissMin(int A[];int n){
int i,*B; //标记数组
B = (int *)malloc(sizeof(int)*n); //分配空间
memset(B,0,sizeof(int)*n); //赋值为0
for(i=0;i<n;i++){
if(A[i]>0 && A[i]<=n){
B[A[i]-1] == 1;
}
}
for(i=0;i<n;i++){
if(B[i] == 0){
break;
}
return i+1;
}
return n+1;
}
---------------------------------------------------------------------------------------------------------------------------------------
/*2019
设线性表L=( a1,a2,a3,...,a(n-2),a(n-1),an )采用带头结点的单链表保存,链表中结点定义如下:
typedef struct node{
int data;
struct node *next;
}NODE;
设计一个空间复杂度为O(1)且时间上尽可能高效的算法重新排列L中的各结点得到线性表L'( a1,an,a2,a(n-1),a3,a(n-2),... )
*/
/*思路:
观察俩个线性表发现L'是由L摘取第一个元素再摘取倒数第一个元素······依次合并而成的
为了方便在链表后半段取元素需要先将L后半段原地逆置否则每取一个尾部的结点都要几乎完整遍历一遍链表
1、先找出链表L的中间结点为此设置两个指针p和q指针每次走一步指针q每次走两步当指针q到达链尾时指针p正好在链表的中间结点
2、将L的后半段结点原地逆置
3、从单链表前后两段中依次各取一个结点按要求重排
*/
void change_list(NODE *h){
NODE *p,*q,*r,*s;
p=q=h;
while(q->next != NULL){ //寻找中间结点
p = p->next;
q = q->next;
if(q->next != NULL){
q = q->next;
}
}
q = p->next;
p->next = NULL;
while(q != NULL){ //将链表后半段逆置
r = q->next;
q->next = p->next;
p->next = q;
q=r;
}
s = h->next; //s指向前半段的第一个数据结点即插入点
q = p->next; //q指向后半段的第一个数据结点
p->next = NULL;
while(q != NULL){ //将链表后半段的结点插入到指定位置
r = q->next; //r指向后半段的下一个结点
q->next = s->next; //将q所指结点插入到s所指结点之后
s->next = q;
s = q->next; //s指向前半段的下一个插入点
q = r;
}
}
---------------------------------------------------------------------------------------------------------------------------------------
/*2020
定义三元组(a,b,c)(均为整数)的具里D=|a-b|+|b-c|+|c-a|
给定三个非空整数集合S1、S2、S3,按升序分别存储在3个数组中
设计算法计算并输出所有可能的三元组(a,b,c)中的最小距离(a,b,c分别为S1,S2,S3中的整数)
例如S1={-1,0,9},S2={-25,-10,10,11},S3={2,9,17,30,41},则最小距离为2相应的三元组为(9,10,9)
*/
---------------------------------------------------------------------------------------------------------------------------------------
/*2021
已知无向连通图G由顶点集V和边集E组成|E|>0
当G中度为奇数的顶点个数为不大于2的偶数时G存在包含所有边且长度为|E|的路径(成为EL路径)
设图G采用邻接矩阵存储类型定义如下
Typedef struct{ //图的定义
int numVertices,numEdges; //图中实际的顶点权和边数
char VertticesList[MAXV]; //顶点表。MAXV为已定义常量
int Edge[MAXV][MAXV]; //邻接矩阵
}:MGraph;
设计算法:int IsExistEL(MGraph G),判断G是否存在EL路径若存在则返回1否则返回0
*/
/*思路
对于采用邻接矩阵存储的无向图,邻接矩阵每一行(列)中非零元素的个数为本行(列)对应的顶点的度。
因此可以依次计算连通图G中各顶点的度并记录度为奇数的顶点个数若个数为0或2则返回1否则返回0
*/
int IsExistEL(MGraph G){
int degree,i,j,count=0;
for(i=0;i<G.numVertices;i++){
degree = 0;
for(j=0;j<G.numVertices;j++){
degree += G.Edge[i][j];
if(degree % 2 != 0){
count++;
}
}
}
if(count ==0 || count == 2){
return 1;
}
else
return 0;
}