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

数据结构–单链表的经典算法题02

头插防断链;尾插留尾针

文章目录

1、试编写带头结点的单链表L中删除一个最小结点的高效算法(假设最小值是唯一的)

算法思想1:(四个指针的情况)

p指针(遍历链表)、pre指针(指向p的前驱)、minp(标记最小值结点)、minpre指针(用来标记最小值的前驱);
若p->datadata,将p和pre分别赋值给minp,minpre指针;
扫描完成后,minp指向的是最小值结点,minpre指向的最小值结点的前驱,将minp结点删除即可

void  Del_Min(LinkList &L) {
	LNode *p=L->next;	//遍历当前链表 
	LNode *pre=L;		//指向p的前驱 
	LNode *minpre=pr,*minp=p; 	//minp用来指向当前链表的最小值结点 
	//minpre指向最小值结点的前驱,方便后序结点的删除 
	//查找出最小值的结点 
	while(p!=NULL){
		if(p->data<minp->data){			//当前遍历的结点,若比min指针所指的结点还要小的时候, 
			minp=p;						//将minp和minpre值修改为p和pre 
			minpre=pre;
		}
		pre=p;							//当前结点判断完后,p和pre指针继续向后移动 
		p=p->next;
	}
	//删除最小值的结点
	minpre->next=minp->next;   //minp的前驱指向minp的后继,修改结点指向 
	free(minp);	//将最小值的结点删除,释放空间 
}

算法思想2:只需要两个指针;

*p指针用来遍历单链表,*minp指针用来指向最小值的结点

  1. 遍历单链表,初始化指针
  2. 若p->datadata时,minp=p;
  3. 判断完成后p指针后移
  4. 利用王道的偷天换日思想,对minp结点进行删除操作
void  Del_Min(LinkList &L) {
	LNode *p=L->next;	//遍历当前链表 
	LNode *minp=p; 	//minp用来指向当前链表的最小值结点  
	LNode *q;		//临时指针,用来删除结点,释放内存空间 
	while(p!=NULL) {
		if(p->data<minp->data){		//移动minp到p结点 
			minp=p; 
		}
		p=p->next;
	}
	//偷天换日思想:利用当前找到的最小值的结点minp,然后minp后继结点的值赋值给minp,将minp指向的结点的后继删除
	 //删除结点的操作 
	 q=minp->next;
	 minp->data=q->data;	//用minp后继结点的值来替换minp结点的值,并将minp的后继结点删除
	 minp->next=q->next;
	 free(q);
}

2、头插法建立单链表(生成的顺序是与输入的序列是相反的)

思想:

  • 首先动态申请创建一个头结点;
  • 输入要添加的元素x
  • 动态申请一个结点用于插入*s,s->data=x;
  • 加入 s->next=L->next; L->next=s;
LinkList * Head_CreatLinkList(LinkList &L){
	int x;
	LNode *s;
	L=(LinkList *)malloc(sizeof(LNode));	//创建一个头结点 
	scanf("%d",&x);
	while(x!=-1){	//当输入的值为-1时,停止插入 
		s=(LNode *)malloc(sizeof(LNode)) ;	//动态一个结点 
		s->data=x;
	    s->next=L->next;			//进行插入操作,修改指针指向
	    L->next=s;
	    scanf("%d",&x);
	}
}

3、尾插法建立单链表(生成的顺序是与输入的序列是相反的)

算法思想:

  • 用一个指针一直指向表尾指针
  • 利用表尾指针和新结点进行修改指向
//尾插法
LinkList List_TailInsert(LinkList &L){
    int  x;
    L=(LinkList) malloc(sizeof (LNode));//建立头结点,初始化一空表
    LNode *s,*r=L;          //r为表尾指针
    scanf("%d",&x); //输入结点的值
    while (x!=1000){    //当输入1000代表结束
        //在r结点之后插入x元素
        s=(LNode *) malloc(sizeof (LNode));
        s->data=x;
        r->next=s;      
        r=s;            //r指向新的表的尾结点
        scanf("%d",&x); //输入结点的值
    }
    r->next=NULL;           //为几点指针置为空
    return L;
}

4、试编写算法将带头结点的单链表就地逆置,要求辅助空间为O(1)

算法思想1:利用头插法的思想对单链的元素进行逆置

//头插法置逆单链表 
LinKList reverse_LinkList(LinkList &L){
	LNode *p,*s;  //p指针用来遍历单链表,s指针用来p的后继防止断链 
	p=L->next;
	L->next=NULL;	//将表头结点置为空 
	while(p!=NULL){
		r=p->next; 		//保留后继防止断链 
		p->next=L->next;
		L->next=p;
		p=r;	
	}
}

算法思想2:三个指针来实现

pre指针:p的前驱
p指针:遍历单链表
r指针:p的后继
只需要修改pre和p的指向,然后将三个指针同时向后移动一个单位,最后只需将p->next=pre,L->next=pre即可

//头插法置逆单链表 
LinKList reverse_LinkList(LinkList &L){
	LNode *p,*pre,*r;  //p指针用来遍历单链表,r指针用来p的后继,pre指针用来指向p的前驱 
	//初始化指针 
	p=L->next;
	r=p->next;			//r指向p的后继 
	pre=NULL;
	while(r!=NULL){		//r等于NULL的时候结束 
		p->next=pre;//修改pre和p的指向
		//pre,p,r向偶移动一个单位 
		pre=p;
		p=r; 
		r=r->next;
	} 
	//当r是NULL的时候,并没有修改pre到p的指向 
	p->next=pre  		//修改pre到p的指向 
	L->next=p;			//将表头结点指向p 
}

5、设在一个带表头结点的单链表中所有元素的结点的数据值无序,试编写个函数,删除表中所有介于给定的的两个值(作为函数的参数给出)之间的元素的元素(若存在)。

算法思想:
需要两个指针:*pre(用来指向p的前驱)、*p(用来遍历链表)*q(用来删除结点)
当p指针指向的结点的值在给定的两个值之间的时候,用pre->next=p;p指针后移,然后释放要删除的结点。

void Del_sk(LinkList &L,int s,int k)	//删除单链表在s和k之间的结点
{
	LNode *q,*pre=L,*p=L->next;
	while(p!=NULL){
		if(p->data>=s&&p->data<=k)
		{
			q=(LNode *)malloc(sizeof(LNode));	//动态申请一个结点,用来释放要删除的结点
			q=p;						
			pre->next=p->next;				//修改指针指向跳过删除结点 
			pre=p;							//后移pre指针 
			p=p->next;						//后移p指针 
			free(q); 						//释放待删除的结点 
		}
	}
 } 

6、给定两个单链表,编写算法找出两个单链表的公共结点

在这里插入图片描述
算法思想1:暴力求公共结点(当两个链表遍历的结点相等时,即为公共结点)
时间复杂度:O(la.length*lb.length)

 LNode * Search_Common(LinkList La,LinkList &\Lb){
 	//初始化 
 	LNode *la=La->next;		
 	LNode *lb=Lb->next;
 	if(la==NULL||lb==NULL) return NULL;
 	while(la!=NULL){
 		while(lb!=NULL){
		 	if(la==lb)  return la; //相同的时候,返回结点 
		 	lb=lb->next;
		 } 
		 la=la->next;	//la指针后移 
		 lb=Lb->next;	//重新指向Lb链表的第一个元素,进行下一轮循环 
		 
	 }
	 return NULL;
 } 

算法思想2:利用两链表的长度差k,将较长的链表指针往后移动k位,然后对两链表的指针进行同步移动,进行判断即可。
时间复杂度:O(la.length+lb.length)

 LNode * Search_Common(LinkList La,LinkList Lb){
 	//初始化 
 	LNode *la=La->next;		
 	LNode *lb=Lb->next;
 	//求两链表的长度  
 	int a=Length(La);
 	int b=Length(lb);
 	//将较长的链表向后移动k位 
 	if(a>b){
 		k=a-b;
 		while(k>0){
 			la=la->next; 
 			k--;
		 }
	 }
	 else{
	 	k=b-a;
 		while(k>0){
 			lb=lb->next; 
 			k--;
		 }
	 }
	 //最后,进行同步比较
	 while(la!=NULL){
	 	if(la==lb)
	 		return la;
	 	la=la->next;
	 	lb=lb->next;
	 }
	 return NULL;
 } 

7、将一个带头结点的单链表A分解为两个带头结点的单链表A和B使得A表中含有原表中序号为奇数的元素,而B表中含有原表中序号为偶数的元素,且保持其相对顺序不变。

算法思想01:不断的使用尾插法(保证相对顺序保持不变),依次生成链表A,B;设置一个计数器i(用来判断是奇数还是偶数结点)

LinkList Split_tow(LinkList &A){
	LNode *la=A->next;	//la用来遍历A链表 
	LNode *ra,*rb;
	int i=1;	//用来技术
	LinkList B =(LinkList )malloc(sizeof(LNode));	//创建一个结点
	A->next=NULL;
	B->next=NULL;
	ra=A;
	rb=B;
	
	while(la!=NULL){
		if(i%2==0)	//插入到B链中--尾插 
		{
			rb->next=la;
			rb=la;
		} 
		else{	//插入到A链中 --尾插 
			ra->next=la;
			ra=la;
		} 
		la=la->next; 
		i++;
	}
	//将最后链表AB的最后一个结点指向NULL 
	ra->next=NULL;
	rb->next=NULL;
	return B; 
}

算法思想02:不断的使用尾插法(保证相对顺序保持不变),依次生成链表A,B;顺序的分别给AB插入

LinkList Split_tow(LinkList &A){
	LNode *p=A->next;	//la用来遍历A链表 
	LNode *ra,*rb;
	LinkList B =(LinkList )malloc(sizeof(LNode));	//创建一个结点
	A->next=NULL;
	B->next=NULL;
	ra=A;
	rb=B;
	
	while(p!=NULL){
		//奇数肯定是尾插  1357.. 
		ra->next=p;
		ra=p;
		p=p->next;
		if(p!=NULL){	//偶数定是 尾插到B中 2468..
		 	rb->next=p;
			rb=p;
			p=p->next;	
		} 
	}
	//将最后链表AB的最后一个结点指向NULL 
	ra->next=NULL;
	rb->next=NULL;
	return B; 
}

8、设C={al,bl,a2,b2,…an,bn}为线性表)采用带头结点的hc单链表存放,设计一个就地算法,将其拆分为两个线性表,使得A={al,a2,…,an} ,B{bn,…,b2,b1}。(结合7)

算法思想:对与a数列而言,采用尾插法(保证相对顺序保持不变)(保证相对顺序保持不变),对与b数列而言采用头插法(保证逆序);依次生成链表A,B。

int Split_tow(LinkList &CLinkList &B,LinkList &A){
	LNode ra;			//尾指针 
	LNode *p=C->next;	//p用来遍历A链表 	
	LNode *r;			//防止头插的时候断链 
	 B =(LinkList )malloc(sizeof(LNode *));	//创建一个头结点的单链表 
	 A =(LinkList )malloc(sizeof(LNode *));	//创建一个头结点的单链表 
	A->next=NULL;	//用尾插 
	B->next=NULL;	//用头插 
	ra=A;
	if(p==NULL) return 0; 
	while(p!=NULL){
		ra->next=p;
		ra=p;
		p=p->next;
		if(p!=NULL){
			r=p->next;
			//头插B
			p->next=B->next;
			B->next=p;
			p=r;		//防断链 
		} 
	}
	//使A链的最后一个结点指向NULL
	ra->next=NULL; 
	return 1;
}

9、在一个递增有序的线性表中,有数值相同的元素存在。若存储方式为单链表,设计算法去掉数值相同的元素,使表中不再有重复的元素。

递增有序;相同元素一定相邻
算法思想1:pre指针表示p的前驱,p指针用来遍历

void Del_SV(LinkList &L){
	//初始化结点 
	LNode *p=L->next->next;
	LNode *temp,*pre=L->next;
	while(p!=NULL){
		if(p->data==pre->data){	//找到元素重复的结点 
			temp = (LNode)malloc(sizeof(LNode));
			temp=p;
			pre->next=p->next;	//删除p结点 
			p=p->next;			//此时不需要移动pre指针
			free(temp);
		}
		else{	//不是相同的,pre和p指针向后移动 
			pre=p;
			p=p->next;
		}
	} 
} 

算法思想2:偷天换日(一个指针)

10、假设有两个按元素值递增次序排列的线性表,t均以单链表的形式存储。请编写算法将这两个单链表归并为一个按元素值递减次序排列的单链表,并要求利用原本两个单链表的结点存放归并后的单链表。

算法思想:用两个指针分别指向两个单链表,然后将一个表置为空,将两指针较小的元素利用头插法插入到空表中

void Merge_LinkList(LinkList &La,LinkList &Lb){
	LNode *la=La->next;
	LNode *lb=Lb->next;
	LNode *r;	//防止断链 
	La->next=NULL;		//将所有的元素都插入到La链表中,用来原来的存储空间
	//第一中情况 :La和Lb都一样长 
	while (la!=NULL&&lb!=NULL){	 
		if(la->data>lb->data){	//将Lb链表中的元素插入到La中(头插法) 
			r=lb->next; //记录lb,防断链 
			lb->next=La->next; //lb结点头插到La中 
			La->next=lb;
			lb=r; 	//
		} 
		else{		//la->data小的时候 
			r=la->next;
			la->next=La->next;
			La->next=la;
			la=r;
		} 
	}
	//第二种情况,两条链表不一样长,只要将剩余的元素插入到La中 
	while(la!=NULL){	//la链表有剩余时 
		r=la->next;//防断链
		la->next=La->next;
		La->next=la; 
		la=r;
	}
	while(lb!=NULL){		//若lb链表有剩余时 
		r=lb->next;//防断链
		lb->next=La->next;
		La->next=lb; 
		la=r;
	}
} 
  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值