关于递归删除链表节点为什么不会断链问题解释

问题的由来:

   当你第一次实现用递归实现链表删除功能的时候,是否有一丝丝的考虑过。这个问题呢?为什么对于非递归版本的删除必须要知道当前要删除节点的前驱,而需要对其前驱节点的next域指针进行修改。而递归删除却不需要呢?难道这样不会造成链表的断链吗?

好了。我们开始抽象出我们今天要解决的问题。

问题一:

递归实现链表节点的删除和非递归删除的区别是什么?

问题二:

为什么在使用递归删除的时候链表不会断链?

先给个代码,好根据代码模拟,不会空想。

函数的递归模型:

终止条件: f(L,x) = 不做任何事                         若L为空表

递归主体:  f(L,x) = 删除*L结点;f(L->next,x);           若L->data == x

f(L,x) = f(L->next,x);                      其他情况

/*
    用递归实现对没有头结点的链表实现删除给定数字
    输出最终结果
*/

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

typedef int ElemType;
const int MaxSize = 100;

typedef struct LNode{
     ElemType data;
     struct LNode *next;
}LNode,*LinkList;

//递归删除链表节点函数
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);
        //printf("if2--> %d\n",L->data);
     } else {
         Del_X_3(L->next,x);
         //printf("else--> %d\n",L->data);
     }

     //printf("--->%d\n",L->data);
}

int main(int argc,char **agrv) {
    int n;
    while(~scanf("%d",&n)) {
         int x;
         LinkList L = NULL;

         LNode *s, *r = L;
         for(int i = 0;i < n;++i) {

             scanf("%d",&x);
             s = (LNode *)malloc(sizeof(LNode));
             s->data = x;

             if(L == NULL) L = s;
             else r->next = s;
             r = s;
             //printf("-->%u\n",r);
         }
         r->next = NULL;

         printf("Please enter you want to delete number: ");
         scanf("%d",&x);

         LNode *p = L;
         while(p) {
             printf("%ox ",p);
             p = p->next;
         }
         puts("");

         Del_X_3(L,x);

//         //test
         p = L;
         while(p) {
             printf("%ox %d\n",p,p->data);
             p = p->next;
         }
         puts("");

    }
    return 0;
}

先解决问题一:

对于非递归版本的删除链表结点,必须要知道其前驱结点。假设当前要删除的结点为p,前驱结点为q。修改代码如下:q->next = p->next;而从上面的代码可以看出,对于递归版本的程序并不需要特别的知道其前驱结点。

再解决问题二:

首先,我们要先明确一个问题。就是上面给出的程序是用引用的。这说明了函数是传址调用。这就是当删除一个结点时候,不用需要知道前驱结点也可以的根本所在。

给个例子模拟一下你就知道了:

3 //输入三个数

3 4 5

4 //删除4

模拟函数调用过程:

初始链表逻辑关系:3  --> 4 --> 5

1、从3结点开始: f(&L,4) ----> 这时候明显不是要删除的数。

所以调用else部分。

L1->next引用传址。(当前的L表示的是结点3)

2、4结点开始: f(&L,4) -----> 这时候发现4就是要删除的数。

调用if2部分

处理代码为:L2 = L2->next(发现问题吗?)

(L1和L2同表示L,为了好说明特别加以区别加了下标。)

其实,L2 == L1->next(即L1->next为结点3的next域)

而执行L2= L2->next现在就相当于把3的next域指向了5                          的指针域。(L1->next = L2->next。所以,我们在这个                          删除的过程中还是隐含的知道了要删除结点的前驱结点)

即,现在的逻辑关系变为:3->5

后面的就都一样了,就不在详说了。程序一直执行到if1                          条件满足为止,然后开始递归返回值。最后终止。

而这个过程是传址的。所以,这回影响最终的结果。

好了。两个问题都完美解决了。虽然,问题不是什么难题。但是,如果对语言和递归没有深刻的掌握还是一时难以理解的。

时间: 2024-07-28 15:50:31

关于递归删除链表节点为什么不会断链问题解释的相关文章

lintcode 容易题:Delete Node in the Middle of Singly Linked List 在O(1)时间复杂度删除链表节点

题目: 在O(1)时间复杂度删除链表节点 给定一个单链表中的表头和一个等待被删除的节点(非表头或表尾).请在在O(1)时间复杂度删除该链表节点.并在删除该节点后,返回表头. 样例 给定 1->2->3->4,和节点 3,返回 1->2->4. 解题: 方法好贱,先把要删除节点后面一个节点的值赋给删除节点,这样需要删除节点就删除了,再把删除节点指向删除节点后面节点的节点 就像这样: node.val = node.next.val; node.next = node.next.

LeetCode之“链表”:在O(1)时间删除链表节点

下边讨论暂不包括尾节点. 一般来说,我们要删除链表中的一个节点是需要知道其上一节点的.但我们真的需要吗? 其实我们可以将待删节点的下一节点的值和指向的下一节点赋予待删节点,然后删除待删节点的下一节点.具体示例程序如下: 1 //O(1)时间删除链表节点,从无头单链表中删除节点 2 void deleteRandomNode(Node *cur) 3 { 4 assert(cur != NULL); 5 assert(cur->next != NULL); //不能是尾节点 6 Node* pNe

剑指offer---以O(1)时间删除链表节点

问题:删除链表节点 要求:以O(1)时间 对于删除指定索引的链表元素大家都很熟悉,思路一般是从头遍历链表直到指定索引位置删除元素,然后维护一下指针即可,时间复杂度O(n).代码如下: 1 // 删除position位置的数据,并返回 2 int List::erasePosition(int position){ 3 if(position<0 || position>(List::size()-1)){ 4 cout<<"position error, please c

面试题18:删除链表节点

18.1 在 O(1) 时间内删除链表节点 <?php header("content-type:text/html;charset=utf-8"); /* * 在 O(1) 时间内删除链表节点. P119 */ class ListNode{ var $val; var $next = NULL; function __construct($x){ $this->val = $x; } } function deleteNode($head,$deleteNode){ i

用递归删除各种节点

#include <stdio.h> #include <malloc.h> #include <stdlib.h> #define MAXSIZE 1000 typedef int ElemType; typedef struct node{ //定义链表节点类型 ElemType data; struct node * next; } Node; typedef Node *LinkedNode; /*[1] 求以h为头指针的单链表的节点个数 */ int GetN

剑指Offer之在O(1)时间删除链表节点

题目描述 给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点. 解题思路 在单向链表中删除一个节点,最常规的做法无疑是从链表的头结点开始,顺序的遍历查找要删除的节点,并在链表中删除该节点.这种思路由于需要顺序查找,时间复杂度自然就是$O(n)$了. 之所以需要从头开始查找,是因为我们需要得到将删除的节点的前面一个节点.在单向链表中,节点中没有指向前一个节点的指针,所以只好从链表的头结点开始顺序查找.那是不是一定需要得到被删除的节点的前一个节点呢?答案是否定的.我们可以很方便

[LeetCode]79. Delete Node in a Linked List删除链表节点

Write a function to delete a node (except the tail) in a singly linked list, given only access to that node. Supposed the linked list is 1 -> 2 -> 3 -> 4 and you are given the third node with value 3, the linked list should become 1 -> 2 ->

剑指offer—第三章高质量代码(o(1)时间删除链表节点)

题目:给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点,链表节点与函数的定义如下:struct ListNode{int m_nValue;ListNode* m_pValue;};void DeleteNode(ListNode** pListNode,ListNode * pToBeDeleted){} 思路:将要删除的节点的下一个节点的值赋值给i,删除下一个节点,但要考虑到删除的节点是最后一个节点并且该链表不止有一个节点的情况和该链表只有一个节点,该节点既是头节点也

在O(1)时间删除链表节点

按照以往的习惯,这种题我们需要遍历整个链表,找到需要删除节点的前一个节点,这样我们就可以操作删除的方法了,这种方法需要O(n)的时间复杂度,但这里要求我们在O(1)的时间下完成.我们只能想其他的方法. 算法思想:其实我们也不需要知道删除节点前一个节点.我们的做法是将删除节点的后边一个节点的值赋给要删除的节点(即把要删除节点的值覆盖),然后将删除节点后边的节点删除(delete)即可.其中我们还需要检查要删除节点的位置,具体问题具体分析,要做到全面. 算法实现: 1 typedef struct