数据结构(单链表的经典算法题03)

数据结构(单链表的经典算法题03)

文章目录

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,…)。要求︰

算法步骤

  • 找中间结点
  • 将后半段的结点逆置(头插)
  • 间隔插入

找中间结点
在这里插入图片描述

代码
在这里插入图片描述
在这里插入图片描述

  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值