设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点(王道课后习题详解)

这道题是王道单科书数据结构的链表课后题大题的第一题,刚开始想了很长时间也想不通,做不会,看了看课后答案也不理解,今天又苦思冥想好大一会终于解决了,下面就是这道链表算法题的核心思想

题干的要求是以:

  1. 递归
  2. 不带头结点

的方式删除单链表中的所有值为x的结点,如果是普通的线性表删除,相信大家都会做,但是在不带头结点的情况以递归的方式删除就比较烧脑。

由于最近时间比较近,来不及画图描述我的思想,我们直接研究答案吧!

上我写的代码 (注意仔细看注释哦):

/*
1.设计一个递归算法,删除不带头结点的单链表 L 中所有值为 x 的结点 
*/
void RecursionDeleteX(LinkList &L, Element x){
    
    
	if(L == NULL)				//若 L 为空表,则直接进行返回 
		return;
	if(L->data == x){
    
    			//若 L->data 为 x, 则对其进行删除 
		LNode *p = L;		//定义指针变量 p 指向当前结点 
		/*
			非常重要!!!
			该结点指针 L 后移,指向下一结点,注意,由于 L指针为引用数据类型,
			所以L指向结点变化时,它上一层结点的next域也将指向下一结点,这样就可以避免删除操作造成的断链现象 
		*/
		L = L->next;			
		
		free(p);	//此时再将该结点删除 
		RecursionDeleteX(L, x);			//继续递归查找删除子链表中值为 x 的结点 
	}else{
    
    
		RecursionDeleteX(L->next, x);	//结点不是x的话就继续递归下层子链表 
	}
}

  1. 第一步,是递归程序的出口,若当前结点 L 此时为 NULL,代表已经遍历完单链表中的所有结点,已完成了对值为x的所有结点的删除操作,返回调用。

  2. 第二步,当该结点不为NULL时,且该结点值为x时,说明该结点需要进行删除!
    删除步骤

    (1). 定义一个临时指针变量 p 指向该结点,准备删除;

    (2). L = L->next; 这一步非常重要, 搞懂了这一步,就是搞懂了本道题的精华,就是使 L 指针进行后移操作,指向下一个结点,即链表断开本结点的连接,再准备继续递归遍历后继结点。

    这时候有小伙伴们应该就会有疑惑了,L= L->next,岂不是丢失了原来的L结点么,那不就断链了,那链表还怎么找得到L之后的后继结点呢,当时我也是卡到了这里,其实该操作并不会导致断链

    原因是我们传递的参数L是引用数据类型,对参数的修改,就是对原变量的修改,也就是说你在本层对L的修改操作,会自动同步到上一层的L->next那里去,即上一层的L->next也指向了L->next->next,这样就保证了链表不断链。

    (3). 使用free函数释放本结点;

    (4). 继续递归调用函数遍历后继结点。

  3. 第三步,当该结点不为NULL时,且该结点值不为x时,继续递归调用函数遍历后继结点!

通过以上三步,就可以使用递归的方法实现对单链表特定值删除啦!!

代码运行截图(删除数据"5"):
在这里插入图片描述

好啦,这就是对这道题的理解,原本还想再将第6题单链表的排序,第8题单链表的公共结点再写写,奈何时间不够用了,现在把代码贴到下面,有兴趣的小伙伴们可以瞅瞅哦!

/*
6.设计一个带头结点的单链表 L , 设计一个算法使其元素递增有序
本题算法思想,将单链表 L 从头部拆分为两个链表,第一个链表初始化仅有一个元素,
故有序,逐渐将第二个链表中的元素插入到第一个链表 L , 使其依旧有序,当全部插入完成后,该链表就实现了排序
类似于 H---->12---->2---->1----->5---->3...... 
L1 = (H---->12---->NULL)				//pre指针作为循环遍历该链表的指针, 用于确定插入位置 
L2 = (2---->1---->5---->3......)		//逐渐将L2的结点按序插入L1即可完成排序, 其中p指向该链表的表头结点, r为p后继结点 
*/
void SortLinkList(LinkList &L){
    
    
	if(L->next == NULL)
		return;
	LNode *p, *pre, *r;			//p 作为 循环遍历链表 L2, pre 作为 循环遍历 L1, r暂存p的后继结点 
	p = L->next;				//实现链表的分割 
	r = p->next;
	p->next = NULL;
	p = r;
	while(p != NULL){
    
    			//循环遍历第二个链表,将其有序插入链表1中 
		r = p->next;
		pre = L;
		while(pre->next!=NULL && pre->next->data < p->data)		//确定插入位置 
			pre = pre->next;
		p->next = pre->next;			//进行插入操作 
		pre->next = p;
		p = r;					// p 指针后移 
	}
}



/*
8.给定两个单链表,编写算法找到两个结点的公共结点 
*/
LinkList GetPublicNode(LinkList &L1, LinkList &L2){
    
    
	if(L1->next==NULL || L2->next==NULL)
		return false;
	int len1 = Length(L1), len2 = Length(L2), dist;
	LinkList short_list, long_list;
	
	if(len1 > len2){
    
    			//确定一个较长的链表和较短的链表,并得到他们的差值 
		long_list = L1->next;
		short_list = L2->next;
		dist = len1-len2;
	}else{
    
    
		long_list = L2->next;
		short_list = L1->next;
		dist = len2-len1;
	}
	
	for(int i=1; i<=dist; i++){
    
    			//根据差值,长链表指针依次后移,目的实现长短链表同步开始寻找公共结点 
		long_list = long_list->next;
	}
	
	while(long_list != NULL){
    
    				//循环遍历寻找公共结点 
		if(short_list->data == long_list->data)
			return long_list;
		short_list = short_list->next;
		long_list = long_list->next;
	}
	return NULL;
}


猜你喜欢

转载自blog.csdn.net/weixin_43479947/article/details/117640319