这道题是王道单科书数据结构的链表课后题大题的第一题,刚开始想了很长时间也想不通,做不会,看了看课后答案也不理解,今天又苦思冥想好大一会终于解决了,下面就是这道链表算法题的核心思想:
题干的要求是以:
- 递归
- 不带头结点
的方式删除单链表中的所有值为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的话就继续递归下层子链表
}
}
-
第一步,是递归程序的出口,若当前结点 L 此时为 NULL,代表已经遍历完单链表中的所有结点,已完成了对值为x的所有结点的删除操作,返回调用。
-
第二步,当该结点不为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). 继续递归调用函数遍历后继结点。
-
第三步,当该结点不为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;
}