数据结构(单链表的经典算法题03)
文章目录
- 数据结构(单链表的经典算法题03)
- 1、设A和B是两个单链表(带头结点),其中元素递增有序。设计一个算法从A和B中的公共元素产生单链表c,要求不破坏A、B的结点。
- 2、已知两个链表A和B分别表示两个集合,其元素递增有序排列。编制函数,求A和B的交集,并存放于A链表中。
- 3、两个整数序列A=a1,a2,a3 ...am和B=b1,b2,b3...,bn已经存入两个单链表中,设计一个算法,判断序列B是否是序列A的连续子序列。
- 4、设计一个算法用于判断带头结点的循环双链表是否对称。
- 5、有两个循环双链表链表头指针分别为h1和h2,编写一个函数将链表h2链接到链表h1之局要求链接后的链表仍保持循环链表的形式。
- 6、有两个循环单链表链表头指针分别为h1和h2,编写一个函数将链表h2链接到链表h1之局要求链接后的链表仍保持循环链表的形式。
- 7、设有一个带头节点的的循环单链表.其结点值均为正整数。设计一个算法,<font face=隶书 size=4 color=red>反复找出单链表中结点值最小的结点并输出,然后将该结点从中删除,直到单链表为空为止,再删除表头结点。([参考第一个](https://editor.csdn.net/md/?articleId=127517609#1L_3))
- 8、修改链表结点的访问频度
- 9、查找单链表中倒数第K个结点(k>0)
- 10、求公共部分
- 11、现要求设计一个时间复杂度尽可能高效的算法,对于链表中data的绝对值相等的结点,仅保留第一次出现的结点而删除其余绝对值相等的结点。
- 12、2019统考真题】设线性表L= (al,a2,a3,...,an-2,an-1,an)采用带头结点的单链表保存,链表中的结点定义如下:请设计一个空间复杂度为O (1)且时间上尽可能高效的算法,重新排列L中的各结点,得到线性表Ll= (al,an,a2,an-1,a3,an-2,......)。要求︰
1、设A和B是两个单链表(带头结点),其中元素递增有序。设计一个算法从A和B中的公共元素产生单链表c,要求不破坏A、B的结点。
算法思想:由于两单链表都是递增有序的,分别用两个遍历指针分别指向A,B,然后依次比较所指的元素,并将元素值较小的指针往后移,若相等,创建一个新的结点存两结点的值,并将其插入到C链中,然后两指针同时向后移,直到表尾。(若其中一个有剩余的时候,不可能是公共元素,无需处理)
LinkList Comm_ValueC(LinkList A,LinkList B){
LNode *p=A->next,*q=B->next; //初始化p、q指针分别指向A,B两个链表
LinkList C=(LinkList )malloc(sizeof(LNode)); //创建一个带头结点的空链表C
LNode *r=C; //r指针用来对C链表进行尾插处理
LNode *s; //用来存放相等结点的data域
While(p!=NULL&&q!=NULL){ //当有一个链表有剩余的时候,不可能是公共元素,无需处理
//当两指针所指结点数据一样,进行尾插到空链C中
if(p->data==q->data){
s=(LNode *)malloc(sizeof(LNode)); //动态申请一个新结点
s->data=p->data; //存放相等结点的data域
//尾插
r->next=s->next;
r=s;
}
//两指针所指结点数据不一样,将较小的结点后移
if(p->data<q->data) p=p->next;
if(p->data>q->data) q=q->next;
}
// 当尾插结束时,应该将尾指针的next置为NULL
r->next=NULL;
return C;
}
2、已知两个链表A和B分别表示两个集合,其元素递增有序排列。编制函数,求A和B的交集,并存放于A链表中。
算法思想:由于两单链表都是递增有序的,分别用两个遍历指针分别指向A,B,然后
将A链置空(方便后续将交集插入到A链中),依次比较两指针所指的元素;若不相等,并将元素值较小的结点释放并往后移;若相等,将结点插入到A链中,然后两指针同时向后移,直到表尾。(若A中有剩余,则逐个释放剩余元素)
LinkList Comm_ValueA(LinkList &A,LinkList B){
LNode *p=A->next,*q=B->next; //初始化p、q指针分别指向A,B两个链表
A->next=NULL;
LNode *r=A; //用来存放相等结点的data域
LNode *temp; //用来删除结点
While(p!=NULL&&q!=NULL){ //当p指针所指链表有剩余的时候,需要将剩余结点释放
//当两指针所指结点数据一样,进行尾插到空链C中
if(p->data<q->data){ //此时,将较小的结点删除,然后后移
temp=p;
p=p->next;
free(temp);
}else if(p->data<q->data) q=q->next; //后移
else{ //此时,元素值相同的时候,直接尾插到A中,然后将p指针后移
r->next=p->next;
r=p;
p=p->next;
}
}
while(p!=NULL){
temp=p;
p=p->next;
free(temp);
}
// 当尾插结束时,应该将尾指针的next置为NULL
r->next=NULL;
return A;
}
3、两个整数序列A=a1,a2,a3 …am和B=b1,b2,b3…,bn已经存入两个单链表中,设计一个算法,判断序列B是否是序列A的连续子序列。
算法思想(暴力法):由于两单链表都是递增有序的,分别用两个遍历指针p、q分别指向A,B,在用一个pre指针用来p指针的回溯;依次比较两指针的值域,若相等,两指针向后移动(当B链表遍历完的时候,匹配成功;当A链表遍历完,匹配失败<每次与q指针不匹配时,p指针回溯到pre的后继>)
int Sub_String(LinkList A,LinkList B){
LNode *p=A->next,*q=B->next; //初始化p、q指针分别指向A,B两个链表
LNode *pre=p; //用来p指针的回溯
while(p!=NULL&&q!=NULL){
if(p->data!=q->data){ //不相等的时候回溯到pre的后继位置,并让q指针回到起始的位置
p=p->next;
pre=p;
q=B->next;
}else{
p=p->next;
q=q->next;
}
}
if(q!=NULL) return 0; //判断是否成功只取决与q是否为空
else return 1;
}
4、设计一个算法用于判断带头结点的循环双链表是否对称。
算法思想:由于是带头结点的循环双链表,利用头结点的前驱q和后继p,比较两指针的数据域(若相等,将p后移,将q前移继续比较),只当p、q两指针处于相邻位置,或者处于同一位置的时候,比较才算成功
/*双链表结构体*/
typedef struct LNode //定义双链表结点类型
{
ElemType data; //每个结点存放一个数据元素
struct LNode *prior,*next; //指针指向下一节点
}DNode,*DList;
//验证是否满足对称性
int Dui_Cheng(DList D){
DNode *p=D->next; //p正向遍历
DNode *q=D->prior; //q反向遍历
while(p!=q&& p->next!=q){
if(p->data==q->data){ //当元素值相同时,继续下一次的判断
p=p->next;
q=q->prior;
}else{
return 0; //失败
}
}
return 1; //成功
}
5、有两个循环双链表链表头指针分别为h1和h2,编写一个函数将链表h2链接到链表h1之局要求链接后的链表仍保持循环链表的形式。
算法思想:由于是带头结点的循环双链表,因此我们需要修改四处指针的指向(顺序是关键)
- 第一处:h2->prior->next=h1;h2的表尾指向h1
- 第二处:h1->prior->next=h2;h1的表尾指向h2
- 第三处:h1->prior=h2->prior;h1的prior指针指向h2的表尾
- 第四处: h2->prior=h1->prior;h2的表头指向h1的表尾
6、有两个循环单链表链表头指针分别为h1和h2,编写一个函数将链表h2链接到链表h1之局要求链接后的链表仍保持循环链表的形式。
算法思想:由于是带头结点的循环单链表,p指针用来记录h1的表尾,q指针用来记录h2的表尾;然后断开重新连接。(关键条件:当p->next==h1时,p所指的结点是表尾)
LinkList Merge(LinkList &h1,LinkList h2){
LNode *p=h1,*q=h2;
//寻找表尾结点
while(p->next!=h1) p=p->next;
while(q->next!=h2) q=q->next;
//修改指针指向
p->next=h2;
h2->next=q;
return h1;
}
7、设有一个带头节点的的循环单链表.其结点值均为正整数。设计一个算法,反复找出单链表中结点值最小的结点并输出,然后将该结点从中删除,直到单链表为空为止,再删除表头结点。(参考第一个)
//第一步:找 第二步:删
void DlALL(LinkList &L){
//指针初始化
LNode *p,*minp;
/*minp用来指向当前链表的最小值结点
minpre指向最小值结点的前驱,方便后序结点的删除 */
LNode *pre,minpre;
//具体操作
while(L->next!=L){ //确保重复,直到删完
//初始化:确保每次都是从头开始
p=L->next;
minp=L->next;
pre=L;
minpre=L;
//一次 删除
while(p!=L) {
if(p-data<minp->data){ //找最小
minp=p;
minpre=pre;
}
pre=p;
p=p->next;
}
printf("%d ",minp->data);//打印
//删除最小值结点,并释放该结点
minpre->next=minp->next;
free(minp);
}
free(L); //释放头结点
}
8、修改链表结点的访问频度
算法思想:找到含x的结点,使freq增加1,取下改结点(根据freq大小)插入到第一个大于freq的后面
void Locate(LinkList &L,int x){ //pred是前驱,fred是频度
LNode *p=L->next,*q
while(p&&p->data!=x){ //查看是否有没有x在单链表中
p=p->next;
}
if(p==NULL) {
//找不到data域为x的结点
printf("不存在值为x的结点");
exit(0);
}
//若果存在等于x的结点,首先将freq频度加1
else{
q=p->pred;//q指向p的前驱
p->freq++; //频度加1
if(p->next!=NULL)
{ p->next->pred; //取下p
p->pred->next=p->next; //查找p的插入位值
}
while(q!=L&&q->fred<=p->fred)//此时q往前找,找到的第一个大的结点的后面就是要插入的位置
q=q->pred;
//注意这四步的顺序
p->next=q->next;
q->next->pred=p;
p->pred=q;
q->next=p;
}
}
9、查找单链表中倒数第K个结点(k>0)
算法思想:先将p指针移动到第k个位置,然后q指向第一个位置,p、q同步移动,当p移动到表尾的时候,q指针指向的结点就是倒数第K个位置
int search_k(LinkList L,int k){
LNode *p=L->next,*q=L;
for(int i=0;i<k;i++){ //将p指针往后移动k个位置
if(p!=NULL){
p=p->next;
}else{
return 0; //不存在
}
}
//p、q两指针进行同步移动
while(p!=NULL){
p=p->next;
q=q->next;
}
return q->data; //返回倒数第k个结点的值
}
10、求公共部分
第一种:就是利用两次while循环,然后一定一动,进行对比
算法思想:
- 首先求出 str1与str2所指链表的长度m,n
- 将较长的链表移动k位(n-m)的绝对值
- 反复将p、q指针后移,直到p=q即为相同后缀
}
int Length(LinkList L){ //求链表的长度
LNode *p=L->next;
int i=1;
while(p!=NULL){
i++;
p=p->next;
}
return i;
}
LNode * search_common(LinkList str1,LinkList str2
{
int m=Length(str1);
int n=Length(str2);
LNode *p=str1->next;
LNode *q=str2->next;
for(p,m>n;m--){//当str1长的时候,移动p
p=p->next;
}
for(q;m<n;n--){//当str2长的时候,移动q
q=q->next;
}
while(p!=NULL&&p!=q){ //同步后移
p=p->next;
q=q->next;
}
//退出上面while循环时的两种情况+判断
if(p==NULL){
return NULL;
}
else{
return p;
}
}
11、现要求设计一个时间复杂度尽可能高效的算法,对于链表中data的绝对值相等的结点,仅保留第一次出现的结点而删除其余绝对值相等的结点。
算法思想:
- (以空间换时间)申请一个大小为n+1的标记数组(初值为0)
- 第一次出现,保留(将标记由0–>1)
- 当标记已经是1的时候,删除改结点
void fun(LinkList &L,int m){
LNode *p=L->next,*pre=L,temp;
int *a=(int *) malloc(sizeof(int)*(m+1)); //申请一个辅助数组,大小为m+1
//初始化标记数组
for(int i=0;i<m+1;i++)
{
a[i]=0;
}
while(p){
if(a[abs(p->data)]==1){ //此时删除改结点
temp=(LNode)malloc(sizeof(LNode));
temp=p;
pre->next=p->next;
p=p->next;
free(temp);
}
else{//标记数组为0的时候,将a[abs(p->data)]置为1
a[abs(p->data)]==1;
pre=p;p=p->next;
}
}
free(a); //释放辅助空间
}
12、2019统考真题】设线性表L= (al,a2,a3,…,an-2,an-1,an)采用带头结点的单链表保存,链表中的结点定义如下:请设计一个空间复杂度为O (1)且时间上尽可能高效的算法,重新排列L中的各结点,得到线性表Ll= (al,an,a2,an-1,a3,an-2,…)。要求︰
算法步骤
- 找中间结点
- 将后半段的结点逆置(头插)
- 间隔插入
找中间结点
代码