链表的递归思路与分析步骤

链表的递归思路与分析步骤:

我们以一道力扣上的经典题目为例子🌰:

此题思路和图片来源:戳这里
删除排序链表中的重复元素
题目描述

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素只出现一次 。

返回同样按升序排列的结果链表。

图片:

Alt
输入:head = [1,1,2,3,3]
输出:[1,2,3]

解题思路:
递归法:

第一步:明白递归函数定义

递归最基本的是要明白递归函数的定义!

递归函数为 deleteDuplicates(head) ,它的含义是处理 head 作为开头的有序链表,使得值出现重复的节点仅保留一个。如果出现多个值相等的节点,我们保留最后一个节点。

第二步:找出递归的终止条件
终止条件就是能想到的基本的、不用继续递归处理的case。

🙈如果 head 为空,那么肯定没有值出现重复的节点,直接返回 head;
🙈如果 head.next 为空,那么说明链表中只有一个节点,也没有值出现重复的节点,也直接返回 head。

一般来说链表递归的终止条件都为head == nullptr || head->next==nullptr

第三步:分析递归的调用条件
什么时候需要递归呢?我们想一下这两种情况:

💕如果 head.val != head.next.val ,说明头节点的值不等于下一个节点的值,所以当前的 head 节点必须保留。
但是 head.next 节点要不要保留呢?我们还不知道,需要对 head.next 进行递归,即对 head.next 作为头节点的链表做处理,使值相等的节点仅保留一个。
然后我们看到self.deleteDuplicates(head) 函数就是做这个事情的!
所以 head.next = self.deleteDuplicates(head.next),这就是递归调用的由来。

💕如果 head.val == head.next.val ,说明头节点的值 等于 下一个节点的值,所以当前的 head 节点必须删除,删除到哪个节点为止呢?
按照函数定义,我们保留值相等的各个节点中最后一个节点,所以 head 到与 head.val 相等的最后一个节点之间的节点也都需要删除;
需要用 move 指针一直向后遍历寻找到最后一个与 head.val 相等的节点。
此时 move 之前的节点都不保留了,因此返回 deleteDuplicates(move);

第四步:找出返回条件
题目让我们返回删除了值重复的节点后剩余的链表,结合上面两种递归调用的情况。

🎃如果 head.val != head.next.val ,头结点需要保留,因此返回的是 head;
🎃如果 head.val == head.next.val ,头结点需要删除,需要返回的是 deleteDuplicates(move)。

如图:
在这里插入图片描述

代码如下:
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(head == nullptr || head->next==nullptr)
           return head; //递归结束条件
        if(head->val != head->next->val)
           head->next = deleteDuplicates(head->next);//如果不相等,则头节点的next指针指向头节点的下一节点为头节点的无重复上升链表“递”的过程
        else
        {
            ListNode *move = head->next;
            while((move->next) && (head->val == move->next->val))
            move = move->next;
            return deleteDuplicates(move);//如果相等,则头节点不保存,头结点需要删除,需要返回的是 deleteDuplicates(move)。

        } 
        return head;  
    }
};
时间复杂度:O(n) 空间复杂度:O(n)
补充:迭代法

由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。

具体地,我们从指针 cur 指向链表的头节点,随后开始对链表进行遍历。如果当前cur与 cur.next对应的元素相同,那么我们就将 cur.next 从链表中移除;否则说明链表中已经不存在其它与 cur 对应的元素相同的节点,因此可以将cur 指向 cur.next。

当遍历完整个链表之后,我们返回链表的头节点即可。

思路来源 戳这里.

细节
当我们遍历到链表的最后一个节点时,cur.next 为空节点,如果不加以判断,访问 cur.next 对应的元素会产生运行错误。因此我们只需要遍历到链表的最后一个节点,而不需要遍历完整个链表。
错误为: runtime error: member access within null pointer of type 'struct ListNode’尝试使用空指针做null->next

代码如下:
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(head == nullptr)
           return head;//特殊条件判定
        ListNode *cur = head;
        while(cur->next!= nullptr)
        /*这里cur—>next的循环判定条件之所以为非空
        1.为了循环
        2.保证cur->next->next有意义,如果cur->next为空的话会报错:
        runtime error: member access within null pointer of type 'struct ListNode'*/
        {
            if(cur->val == cur->next->val)
                cur->next = cur->next->next;//如果相等就找到下一个不相等的节点为止
            else
                cur = cur->next;
        }
        return head;      
    }
};
时间复杂度:O(n) 空间复杂度:O(1)
  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值