(C++语言)算法通关村第一关白银挑战——删除排序链表中的重复元素(leetcode 82)

目录

链表与其他数据结构的对比

题目描述

提交代码

思路

为什么选择链表:

解决方案


链表与其他数据结构的对比

与数组对比:

  • 数组需要在删除元素时做大量数据移动和拷贝,时间和空间复杂度较高
  • 链表只需要修改指针引用,时间和空间效率显著优于数组

与哈希表对比

  • 哈希表需要额外空间存储键值对
  • 插入和删除的时间复杂度也较链表大
  • 不保证元素的顺序性

与二叉搜索树对比:

  • 查找和删除节点时需要递归遍历树结构,编码实现复杂度高
  • 需要大量指针引用,内存开销大

所以简而言之,链表主要优势是:

  • 原生支持按顺序的数据结构
  • 动态长度,插入和删除简单高效
  • 指针引用让修改链表结构变简单
  • 无需大量数据搬移,时间和空间复杂度低

题目描述

                                                        图片来源leetcode官网82题

提交代码

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if (head == nullptr) {
            return nullptr;
        }
        ListNode *dummy = new ListNode(0, head); // 虚拟头结点
        ListNode *cur = dummy;

        while (cur->next && cur->next->next ) {
            if (cur->next->val == cur->next->next->val) {  // 找到重复的元素
                int val = cur->next->val;
                cur->next = cur->next->next;
                while (cur->next && cur->next->val == val) {
                    cur->next = cur->next->next;
                }
            }
            else {
                cur = cur->next; // 指向下一个元素
            }
        }
        return dummy->next;

    }
};

思路

当我第一次看到这个问题时,我脑海里首先浮现出以下几个关键点:

  1. 链表已排序
  2. 要删除重复节点
  3. 重复意味着值相同
  4. 要求不能打乱排序

那么我开始思考如何利用“链表已排序”这个条件来设计解法。排序意味着相同值的节点一定是相邻的。我可以遍历链表,一旦检测到当前节点和下一个节点的值相同,就是重复节点了。

然后我想到使用两个嵌套的while循环:

外层while循环用来遍历整个链表。内层while循环用来遍历连续的重复节点,就是当cur->next->val == cur->next->next->val时,这就代表有重复,然后我可以修改链表链接跳过这些重复节点。

那么问题就转化为:

  1. 如何判断两个相邻节点是否是重复的? 通过值比较即可判断。
  2. 如何删除/跳过重复节点? 我可以直接修改 next 引用,将其指向重复序列的下一个非重复节点

为什么选择链表:

选择链表的结构来解决这个问题,主要是由于链表本身的特点及与题目的契合度最高。

首先,当我第一次看到这个问题时,脑海里浮现出“链表已排序”这个关键条件。排序意味着相同值的节点一定是相邻的。所以我可以利用这个条件来判断重复节点,这成为设计解法的关键起点。

然后,链表天然就是一个动态长度,有序的数据结构,正好符合了本题的输入输出要求。 链表通过指针/引用让节点连接在一起,可以非常容易地实现跳过某些节点的逻辑来删除重复元素,而又不影响链表的结构及元素顺序。

再者,如果使用其他数据结构比如数组,会需要大量元素的搬移操作,既增加实现难度,又降低效率。而链表只需要修改指针引用,简单高效完成删除操作。

最后,链表结构恰好契合了本题数据特征(排序、去重)的技术需求,可以写出非常简洁的解法。其它数据结构要么效率低,要么实现复杂,很难达到链表结构的简单与效率。

解决方案

1. 防御性编程:检查头结点是否为空,为空直接返回

2.初始化虚拟头结点: 首先,创建一个虚拟头结点(dummy),并将其指向原始链表的头结点。这个虚拟头结点的作用是简化边界情况的处理,同时也方便最终返回整理后的链表。

3.遍历链表: 使用指针 cur 遍历链表,检查当前节点 cur->next 和其后继节点 cur->next->next 是否存在。

4.检测重复元素: 如果当前节点和其后继节点的值相等,说明存在重复元素。在这种情况下,需要进行删除操作。

5.删除重复元素: 记录重复元素的值 val,然后调整指针,将当前节点的下一个节点指向下下个节点,即 cur->next = cur->next->next。接着,使用循环删除所有连续重复的节点,直到遇到不重复的节点为止。

6.移动指针: 如果当前节点和其后继节点的值不相等,说明没有重复元素,直接将指针 cur 移动到下一个节点,即 cur = cur->next

7.返回结果链表: 最终返回虚拟头结点的下一个节点,即去除重复元素后的已排序链表。

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值