Bingo, Computer Graphics & Game Developer

递归删除单链表中特定值

理解来自CSDN中的递归删除算法,个人补充细节描述

题为「设计递归算法,删除无头结点的单链表L中值为x的结点」

void deleteX(LinkList &L, ElemType x)  
{  
    LNode *p;  
    if(L==NULL) return;  
    if(L->data==x)  
    {  
        p=L;  
        L=L->next; // (1)单链表保持连续 
        free(p);  
        deleteX(L,x); // (2)递归调用
    }
    else
        deleteX(L->next,x); //(3)向后遍历
}  

理解难度在于似乎仅仅只是释放了当前结点并使得L向前推进,而导致了断链。

例如要在单链表[1,3,5,7][1, 3, 5, 7]中删除元素xx(例如x=3x=3),这里递归第二次调用函数讲会遇到data==3data==3

核心理解在于这里的LinkList L是引用, 观察堆栈会非常清晰的说明这个问题。

deleteX

当代码中(2)(2)处递归调用deleteX时, 参数作为L->next的引用传入(引用为别名,相当于本体操作),即递归中参数L为上一函数的L->next本体,此时的代码相当于

deleteX(L->next, x)  
{  
    // ...
    p = L->next;  
    L->next = L->next->next; // 单链表保持连续 
    free(p);  
    deleteX(L->next, x);
    // ...
}  

如若将引用更换为指针,相较于引用的实现较为繁琐,但更能观察到上述代码工作的本质。

// 仅有传入指针的指针才能改变上一函数中指针的值
void deleteX(LinkList **L, ElemType x)  
{  
    LNode *p;  
    if((*L)==NULL) return;  
    if((*L)->data==x)  // L->next指向的结点为x
    {  
        p=*L;  // p指向L->next指向的结点
        *L=(*L)->next; // L->next=L->next->next
        free(p);  
        deleteX(L,x); // 传入L->next的地址
    }
    else
        deleteX(&((*L)->next),x); // 传入L->next指向的结点的next指针的地址
}