Files
aimto408/408代码汇总(自己总结的,更新至2018年,欢迎提供其他年份的).md
2022-08-31 19:43:34 +08:00

380 lines
13 KiB
Markdown
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.
## **408代码汇总**
1. 2009年
已知一个带有表头结点的单链表,结点结构为:
假设该链表只给出了头指针 list。在不改变链表的前ᨀ下请设计一个尽可能高效的算法查找链表中倒数第 k 个位置上的结点k 为正整数)。若查找成功,算法输出该结点的 data 域的值,并返回 1否则只返回 0。
**算法思想使用pq两个指针p指针先移动扫描k个指针之后q再与p同步移动当p指向最后一个节点时q正好指向倒数第k个节点**
```c
int SearchRearK(LNode *L, int k)
{
int count=0;//用来计数
LNode *q=L->link,*p=L->link;
while(p!=NULL)
if(count<k)
count++;
else
q=q->link;//当count等于开始q和p同步向后移动
p=p->link;
if(count<k)
return 0;//如果链表节点个数小于k
else
printf("%d",q->data);
return 1;
}
```
----
2. 2010年
设将 nn>1个整数存放到一维数组 R 中。试设计一个在时间和空间两方面都尽可能高效的算法。将 R 中保存的序列循环左移 p0<p<n个位置即将 R 中的数据由X0, X1, , Xn-1变换为Xp, Xp+1, , Xn-1, X0, X1, , Xp-1
**算法思想先将R中前p个元素逆置再将剩下的元素逆置最后将R中所有的元素再整体做一次逆置即可**
```c
void Reverse(int R[],int l,int r)
{
int i,j;
int temp;
for(i=l,j=r;i<j;++i,--j)
{
temp=R[i];
R[i]=R[j];
R[j]=temp
}
}
void RCR(int R[],int n,int p)
{
if(p<=0||p>=n)
cout<<"ERROR"<<endl;
else
{
Reverse(R,0,p-1);
Reverse(R,p,n-1);
Reverse(R,0,n-1);
}
}
```
----
3. 2011年
一个长度为LL≥1的升序序列S处在第¬L/2º个位置的数称为S 的中位数。例如若序列S1=1113151719则S1 的中位数是15两个序列的中位数是含它们所有元素的升序序列的中位数。例如若S2=246820则S1 和S2 的中位数是11。现在有两个等长升序序列A 和B试设计一个在时间和空间两方面都尽可能高效的算法找出两个序列A 和B 的中位数
**算法思想较容易想到使用二路归并思想顺序比较S1与S2当S1小于S2中的值S1的下标向后移动一位同理S2小时S2下标向后移动一位当比较次数达到n序列长度返回对应的值就是中位数了**
```c
int Search_M(int S1[],int S2[],int n)
{
int i=j=k=0;
while(i<n&&j<n)
{
k++;
if(S1[i]<S2[j])
{
i++;
if(k==n)
return S1[i-1];
}
else
{
j++;
if(k==n)
return S2[j-1];
}
}
}
```
----
4. 2012年
假定采用带头结点的单链表保存单词,当两个单词有相同的后缀时,则可共享相同的后缀存储空间,设 str1 和 str2 分别指向两个单词所在单链表的头结点,链表结点结构为 ,请设计一个时间上尽可能高效
的算法,找出由 str1 和 str2 所指向两个链表共同后缀的起始位置。
**算法思想先将较长的链表先向后移动k个位置让两个链表之后的扫描长度是一样长的之后两个链表的扫描指针同步向后移动返回第一个公共节点即是两个链表公共后缀的起始位置**
```c
void FindSameRear(LNode *L1,LNode *L2)
{
int len1=Length(L1);
int len2=Length(L2);
LNode *p,*q;
for(p=L1;Len1>len2;len1--)
p=p->next;
for(q=L2;len2>len1;len2--)
q=q->next;
while(p->next!=NULL&&p->next!=q->next)//判断是否访问到同一节点
{
p=p->next;
q=q->next;
}
return p-next;//返回共同后缀的起点
}
```
----
5. 2013年
已知一个整数序列 A=a0, a1, …, an+1其中0≤ain0≤in。若存在ap1=ap2=…= apm=x 且 m> n/2 (0≤pkn1≤k≤m),则称 x 为 A 的主元素。例如 A=0, 5, 5, 3, 5, 7, 5, 5),则 5 为主元素;又如 A=0, 5, 5, 3, 5, 1, 5, 7则 A 中没有主元素假设 A 中的 n 个元素保存在一个一维数组中,请设计一个尽可能高效的算法,找出 A 的主元素。若存在主元素,则输出该元素;否则输出-1。
**算法思想:(较容易理解)计数排序思想,用一个数组记录,每个值出现次数,用的是使用牺牲空间换取时间的做法**
```c
int Majority(int A[],int n)
{
int k,max=0;
int *p;
p=(int*)malloc(sizeof(int)*n);//为数组申请内存空间
for(k=0;k<n;k++)
p[k]=0;//将数组初始化都为0
for(k=0;k<n;k++)
{
p[A[k]]++;
if(p[A[k]]>p[max])
max=a[k];//记录出现次数最多的元素
}
if(p[max]>n/2)
return max;//如果个数大于n/2表示找到了
else
return -1
}
```
----
6. 2014年
二叉树的带权路径长度WPL是二叉树中所有叶结点的带权路径长度之和。给定一棵二叉树 T采用
二叉链表存储二叉树的带权路径长度WPL是二叉树中所有叶结点的带权路径长度之和。给定一棵二叉树 T采用二叉链表存储
**算法思想1.先序遍历:用一个 static 变量记录 wpl把每个结点的深度作为递归函数的一个参数传递若该结点是叶子结点那么变量 wpl 加上该结点的深度与权值之积;若该结点非叶子结点,那么若左子树不为空,对左子树调用递归算法,若右子树不为空,对右子树调用递归算法,深度参数均为本结点的深度参数加 1最后返回计算出的 wpl 即可。**
**2.层次遍历使用队列进行层次遍历,并记录当前的层数,当遍历到叶子结点时,累计 wpl当遍历到非叶子结点时对该结点的把该结点的子树加入队列当某结点为该层的最后一个结点时层数自增 1队列空时遍历结束返回 wpl。**
```c
//先序遍历
int WPL(BTNode *root){
return PreOrder(root, 0);
}
int PreOrder(BTNode *root, int deep)
{
static int wpl = 0; //定义一个 static 变量存储 wpl
if(root->lchild == NULL && root->rchild == NULL) //若为叶子结点,累积 wpl
wpl += deep*root->weight;
if(root->lchild != NULL) //若左子树不空,对左子树递归遍历
PreOrder(root->lchild, deep+1);
if(root->rchild != NULL) //若右子树不空,对右子树递归遍历
PreOrder(root->rchild, deep+1);
return wpl;
}
//层次遍历
int wpl_LevelOrder(BTNode *root)
{
BTNode *q[MaxSize]; //声明队列
front = rear = 0; //头指针指向队头元素,尾指针指向队尾的后一个元素
int wpl = 0, deep = 0; //初始化 wpl 和深度
BTNode *lastNode; //lastNode 用来记录当前层的最后一个结点
BTNode *newlastNode; //newlastNode 用来记录下一层的最后一个结点
lastNode = root; //lastNode 初始化为根节点
newlastNode = NULL; //newlastNode 初始化为空
rear=(rear+1)%Maxsize;
q[rear] = root; //根节点入队
BTNode *p;
while(front != rear)//层次遍历,若队列不空则循环
{
p = q[end1++]; //拿出队列中的头一个元素
if(p->lchild == NULL & p->rchild == NULL)
wpl += deep*p->weight; //若为叶子结点,统计 wpl
if(p->lchild != NULL) //若非叶子结点把左结点入队
{
rear=(rear+1)%Maxsize;
q[rear] = p->lchild;
newlastNode = p->lchild;
} //并设下一层的最后一个结点为该结点的左结点
if(p->rchild != NULL)//处理叶节点
{
rear=(rear+1)%Maxsize;
q[rear] = p->rchild;
newlastNode = p->rchild;
}
if(p == lastNode)//若该结点为本层最后一个结点,更新 lastNode
{
lastNode = newlastNode;
deep += 1; //层数加 1
}
}
return wpl; //返回 wpl
}
```
----
7. 2015年
用单链表保存 m 个整数结点的结构为data且|data|≤nn 为正整数)。现要求设计一个时 间复杂度尽可能高效的算法,对于链表中 data 的绝对值相等的结点,仅保留第一次出现的结点而删除其余绝对值相等的结点。
**算法思想:类似前面的,也是计数排序思想,借助一个数组,具体思想参考前面**
```c
void DeleteABS(LNode *L,int n)
{
LNode *p=L->link,*q;//p指向L下一节点q用来指向被删节点
int *a,m
a=(int*)malloc(sizeof(int)*n);//为数组申请内存空间
for(int k=0;k<n;k++)
a[k]=0;//将数组初始化都为0
while(p!=NULL)
{
m=p->data>0?p->data:-p->data;
if(a[m]==0)
{
a[m]=1;//标记,前面以及有这个数了,
p=p->link;
}
else
{
q=p;
p=p->link;
free(q);
}
}
free(a);//释放掉a的空间
}
```
----
8. 2016年
已知由 nn≥2个正整数构成的集合 A={ak|0≤kn},将其划分为两个不相交的子集 A1和 A2元素个数分别是 n1和 n2A1和 A2中元素之和分别为 S1和 S2。设计一个尽可能高效的划分算法满足|n1-n2|最小且|S1-S2|最大。(全抄答案的,没有自己写)
**算法思想将最小的n/2)个元素放在 A1中其余的元素放在 A2中分组结果即可满足题目要求。仿照快速排序的思想基于枢轴将 n 个整数划分为两个子集。根据划分后枢轴所处的位置 i 分别处理:**
**①若 i=n/2),则分组完成,算法结束;**
**②若 in/2),则枢轴及之前的所有元素均属于 A1继续对 i 之后的元素进行划分;**
**③若 in/2),则枢轴及之后的所有元素均属于 A2继续对 i 之前的元素进行划分;**
```c
int setPartition(int a[], int n)
{
int pivotkey, low=0, low0=0, high=n-1, high0=n-1, flag=1, k=n/2, i;
int s1=0, s2=0;
while(flag)
{
piovtkey=a[low]; //选择枢轴
while(low<high)//基于枢轴对数据进行划分
{
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];
} //end of while(low<high)
a[low]=pivotkey;
if(low==k-1) //如果枢轴是第 n/2 小元素,划分成功
flag=0;
else//是否继续划分
{
if(low<k-1)
{
low0=++low;
high=high0;
}
else
{
high0=--high;
low=low0;
}
}
}
for(i=0;i<k;i++) s1+=a[i];
for(i=k;i<n;i++) s2+=a[i];
return s2-s1;
}
```
----
9. 2017年
请设计一个算法,将给定的表达式树(二叉树)转换成等价的中缀表达式(通过括号反映次序),并输出
**算法思想:基于二叉树的中缀遍历,添加适当括号,显然,表达式的最外层(对于根节点)及操作数(对应叶节点)不需要添加括号(这句是答案说的,其实不太懂)**
```c
void B2E(BTNode *root)
{
B2E(root,1);
}
void B2E(BTNode *root,int deep)
{
if(root==NULL)
printf("NULL");
else if(root->left==NULL&&root->right==NULL)
printf("%s",root->data);//输出操作数
else
{
if(deep>1) printf("(");
B2E(root->left,deep+1);
printf("%s",root->data);//输出操作符
B2E(root->right,deep+1);
if(deep>1) printf(")");
}
}
```
----
10. 2018年
给定一个含nn>=1个整数的数组请设计一个在时间上尽可能高效的算法找出数组中未出现的最小正整数。例如数组-5323中未出现的最小正整数是1数组123中未出现的最小正整数是4。
**算法思想:又是计数排序思想,就不再论述了,用一个数组计数,也就是用空间换时间**
```c
int FindNoMin(int A[],int n)
{
int *B,i
B=(int*)malloc(sizeof(int)*n);//为数组申请内存空间
for(i=0;i<n;i++)
B[i]=0;//将数组初始化都为0
for(i=0;i<n;i++)
if(A[i]>0&&A[i]<=n)//若A属于0~n之间标记B
B[A[i]-1]=1;
for(i=0;i<n;i++)//扫描计数数组,找到目标值
if(B[i]==0)
break;
free(B);
return i+1;
}
```