题目来自王道书:设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点。
这道题在开始看的时候是觉得很简单的,判断是否等于x,然后在L->next进行递归就是了,但是当我看到答案的时候就很懵了(答案如下):
void Del_x_3(Linklist &L,ElemType x)
{
LNode *p;
if(L==NULL)
return;
if(L->data==x)
{
p=L;
L=L->next;
free(p);
Del_x_3(L,x);
}
else
Del_x_3(L->next,x);
}
在王道书的答案中,在if(L->data==x)的程序段里面,没有寻找前驱结点,来改变next域,直接使用了L=L->next,在代码最后,也为我们强调了这段程序不会断链。
那首先我们需要了解,什么是断链:在这个问题中,也就是next没有指向它应该指向的地方。但在王道书上的代码,没有找前驱结点改变其next域,这就会造成断链了呀,为什么没有?这就涉及到了void Del_x_3(Linklist &L,ElemType x)在调用中使用了“引用类型”(Linklist &L)。
什么是“引用类型”,简单来说,就是给你起了个外号,你叫“李**”,别人叫你“小李同学”,这时候“小李同学”和“李**”都是指你,“小李同学”就相当于你的“引用类型”,用程序表示就是ElemType &小李同学=李**,把这个放入实际的代码例子中就相当于int &a=b,这个时候a和b就会指向同一片区域,a和b是这个片区域的不同名字而已。

同时在使用引用类型的时候需要注意以下三个规则:
(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
(2)不能有NULL 引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
//如果想要更加深入的了解引用还有引用和指针的关系可以参考这两篇文章引用和指针的区别 引用类型
讲完引用类型,接下来将为什么在递归中可以使直接删除结点不会导致链表断链。在void Del_x_3(Linklist &L,ElemType x)在调用中使用“引用类型”(Linklist &L)着使得在调用中后一个L与前一个L->next是相同的。我们假设链表元素为{1,2,2,5,6,5,2},我们在其中删除值为2 的元素,在L指向1号元素时,1!=2,因此执行Del_x_3(L->next,x),此时我们将第二轮调用的L称为L1,L1=L->next,同时L1指向了2号元素2= =2,因此需要删除,在执行L=L->next时,相当于L1=L1->next,相当于L->next=L->next->next,这下是不是和我们所熟知的式子一样了。

如图一样,L的next域(即我红色那一部分)和L1都指向了后继结点,同时,L1和L->next是共享一个数据块的,这两是同一个数据块的不同名字,所以L=L->next在我看来还有另外一个理解,就是L所代表的数据块被L->next所覆盖,而L所代表的数据块正式前驱结点的next域,即L的前驱结点next域也同时被修改了,这样便没有造成断链。之后的也是一样的,可以自己推一下。同时需要注意,在每一次递归后其实与当前L相关的只有本身和前驱结点的next域,与再之前结点并没有什么关系。
本文探讨了如何使用递归算法删除不带头结点的单链表中所有值为x的结点。王道书中的解决方案通过引用类型避免了寻找前驱结点来修改next域,从而防止断链。讲解了引用类型的特性,并通过实例解释了为何在递归过程中可以直接删除节点而不造成链表断裂。
6954

被折叠的 条评论
为什么被折叠?



