力扣82删除排序链表中的重复元素 II:思路分析+代码实现+方法总结(三指针法快慢指针法【双指针】递归法)

文章目录

  • 第一部分:题目描述
  • 第二部分:代码实现
    • 2.1 三指针法
    • 2.2 快慢指针法(双指针)
    • 2.3 递归

第一部分:题目描述

🏠 链接:82. 删除排序链表中的重复元素 II - 力扣(LeetCode)

⭐ 难度:中等

image-20230515032300743

第二部分:代码实现

2.1 三指针法

p1 是待删除的上一个节点,每次循环对比 p2、p3 的值。

  • 如果 p2 与 p3 的值重复,那么 p3 继续后移,直到找到与 p2 不重复的节点,p1 指向 p3 完成删除。
  • 如果 p2 与 p3 的值不重复,p1,p2,p3 向后平移一位,继续上面的操作。
  • p2 或 p3 为 null 退出循环。
    • p2 为 null 的情况,比如链表为 1 1 1 null。
p1 p2 p3
s, 1, 1, 1, 2, 3, null

p1 p2    p3
s, 1, 1, 1, 2, 3, null

p1 p2       p3
s, 1, 1, 1, 2, 3, null

p1 p3
s, 2, 3, null

p1 p2 p3
s, 2, 3, null

   p1 p2 p3
s, 2, 3, null

代码:

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        // 当链表无节点或者只存在头节点,直接返回 null / 头节点
        if (head == null || head.next == null) {
            return head;
        }

        // 设置一个哨兵节点(伪头节点)
        ListNode sentinel = new ListNode(-999, head);
        // p1 指向哨兵节点
        ListNode p1 = sentinel;
        ListNode p2, p3;

        // 设置 p2 为 p1 的下一个节点,p3 为 p1 的下下个节点
        // 如果 此时 p2 或 p3 为空节点说明已遍历完链表,再无重复节点
        while ((p2 = p1.next) != null
                && (p3 = p2.next) != null) {

            // 如果 p2 与 它的下一个节点val值重复,则需要删除当前p2、p3指向的节点以及后面与p2.val相同值的节点
            if (p2.val == p3.val) {

                // 使用 p3 继续向后遍历,直到找到与p2.val不相同值的节点
                while ((p3 = p3.next) != null && p3.val == p2.val) {
                }

                // 此时 p3 指向的节点为不想同值的节点

                // 为了跳过当前 p2 到 p3以前 的节点,可以将p2的上一个节点p1的next 指向p3
                p1.next = p3;

                // 继续下一次循环即可,在循环条件中再次对p2、p3赋值使得p2、p3分别指向p1的下个节点与下下个节点
            } else {
                // 如果 p2 与 它的下一个节点p3的val值不重复,说明链表中不存在与 p2.val 相等的值的节点,
                // 则只需要将 p1、p2、p3后挪一位即可,由于在外层while循环中会对p2、p3赋值,因此在这里只需要对p1后移即可
                p1 = p1.next;
            }
        }

        // 返回真正的头节点
        return sentinel.next;
    }
}

2.2 快慢指针法(双指针)

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        // 如果链表无节点或者只有一个节点,则返回 null / head
        if (head == null || head.next == null) {
            return head;
        }

        // 设置一个哨兵 sentinel,它的下一个节点为链表头节点 head
        ListNode sentinel = new ListNode(-999, head);
        // 快指针,指向 head
        ListNode fast = head;
        // 慢指针,指向 哨兵
        ListNode slow = sentinel;
        // 记录某个值出现次数
        int count = 0;

        // 使用快指针进行遍历链表
        while (fast != null) {
            // 如果快指针指向的val与慢指针下一个节点的val相等
            if (fast.val == slow.next.val) {
                // 则fast继续向后遍历
                fast = fast.next;
                // 值出现次数 +1
                count++;
            } else {
                // 如果fast遇到与慢指针下一个节点的val不重复的值

                // 判断 慢指针下一个节点的val出现次数即 count 是否为1
                if (count == 1) {
                    // 如果只出现了一次,说明未出现重复的值,则慢指针向后移动一位即可
                    // 此时fast的位置一定是在 slow 的后面一位
                    slow = slow.next;
                } else {
                    // 如果出现了多次,则需要忽略所有与 slow.next.val 具有相等值的节点
                    // 如何忽略呢?只需要更新slow的下一个节点为fast即可
                    slow.next = fast;
                }

                // 遇到了不重复的值,做完了对上一个count的处理后,重新清0进行下一次计算
                count = 0;
            }
        }

        // 当 fast 为空时会退出循环,但并未对count进行后续处理,因此这里还需要处理一下
        // 如果不为1,说明出现了重复节点,如何忽略这些重复节点呢?只需要更新slow的下一个节点为null即可
        if (count != 1) {
            slow.next = null;
        }

        // 返回真正的头节点
        return sentinel.next;
    }
}

2.3 递归

递归函数负责返回:从当前节点(我)开始,完成去重的链表。

  1. 若我与 next 重复,一直找到下一个不重复的节点,以它的返回结果为准。
  2. 若我与 next 不重复,返回我,同时更新 next。
// 链表:1 -> 1 -> 1 -> 2 -> 3

deleteDuplicates(ListNode p = 1) {
    // 找下个不重复的
	deleteDuplicates(ListNode p = 1) {
        deleteDuplicates(ListNode p = 1) {
			deleteDuplicates(ListNode p = 2) {
                2.next=deleteDuplicates(ListNode p = 3) {
					// 只剩一个节点,返回
                    return 3
                }
                return 2
			}
        }
    }
}

代码:

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        // 边界节点的考虑
        if (head == null || head.next == null) {
            return head;
        }

        // 如果节点的val重复,则删除所有为val的节点
        if (head.val == head.next.val) {
            // 找到下下个节点
            ListNode tmp = head.next.next;
            // 循环遍历后面节点直到找到一个与当前节点val不同的节点
            while (tmp != null && tmp.val == head.val) {
                tmp = tmp.next;
            }

            // 找到的tmp就是与 head 取值不相同的节点
            // 直接舍弃从head开始的这一部分节点,
            // 从 tmp 开始继续递归调用,return返回的节点一般是作为某个节点的下一个节点以达到舍弃的效果
            return deleteDuplicates(tmp);
        }

        // 如果不重复,就更新它的next节点,实际是对后续的节点进行去重操作
        head.next = deleteDuplicates(head.next);
        // 更新next后,返回当前这个不重复的节点即可
        return head;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/20616.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT热门资料汇总,绝对不割韭菜

前言 ChatGPT 的出现,AI圈子一下就热闹起来了,各个公司争先恐后地出自己的产品,百度的文心一言、谷歌的Bard、阿里的通义千问等等,有很多人借此机会已经赚到百万,很多卖课搞培训的都是互为合伙人,大家都懂…

如何注册appuploader账号​

如何注册appuploader账号​ 我们上一篇讲到appuploader的下载安装,要想使用此软件呢,需要注册账号才能使用,今​ 天我们来讲下如何注册appuploader账号来使用软件。​ 1.Apple官网注册Apple ID​ 首先我们点击首页左侧菜单栏中的“常见网…

【更新日志】填鸭表单TduckPro v5.1 更新

hi,各位Tducker小伙伴。 填鸭表单pro迎来了v5.1版本;本次我们进行了许多的功能新增和优化,能够让我们在日常使用中获得更好的体验。 让我们一起来康康新功能吧。 01 新增Pro功能 新增登录后才能填写表单。 新增表单卡片一键发布。 新增矩…

【STM32CubeMX】F103窗口看门狗

前言 本文记录了我学习STM32CubeMX的过程,方便以后回忆。我们使用的开发板是基于STM32F103C6T6的。本章记录了窗口看门狗的使用配置。要学习的话,注意流程一说,省略的内容。 基础 窗口看门狗(WWDG)属于APB1上外设。窗口看门狗(WWDG)的时钟源…

JUC并发编程16 | CAS自旋锁

CAS自旋锁 是什么,干什么,解决了什么痛点?如何解决,如何使用。 原子类:java.util.concurrent.atomic 在没有CAS之前,多线程环境不使用原子类保证线程安全i等操作,会出现数据问题,…

Day968.如何开启一个遗留系统现代化项目? -遗留系统现代化实战

如何开启一个遗留系统现代化项目? Hi,我是阿昌,今天学习记录的是关于如何开启一个遗留系统现代化项目?的内容。那如何启动一个遗留系统现代化项目。 一、项目背景 说来有点唏嘘,国内遗留系统的重灾区,恰恰…

WiFi(Wireless Fidelity)基础(八)

目录 一、基本介绍(Introduction) 二、进化发展(Evolution) 三、PHY帧((PHY Frame ) 四、MAC帧(MAC Frame ) 五、协议(Protocol) 六、安全&#x…

迪赛智慧数——柱状图(象形标识图):在选择另一半时,你更看重的是?

效果图 好看只排第六,第一确实众望所归!当代男女择偶标准出炉,一张图带你看清。 女性挑选另一半时,她们更看重伴侣收入高、职业体面、工作能力强、受教育程度高,还得和自己有共同话题。 男性择偶观和女性恰恰相反&am…

ctfshow周末大挑战2023/5/12

本周周末大挑战用到的函数讲解 parse_url() 作用:解析URL,返回其组成部分 语法: parse_url ( string $url [, int $component -1 ] ) 参数: url:要解析的 URL。无效字符将使用 _ 来替换。 component: …

软件测试月薪2万,需要技术达到什么水平?

最近跟朋友在一起聚会的时候,提了一个问题,说一个软件测试工程师如何能月薪达到二万,技术水平需要达到什么程度?人回答说这只能是大企业或者互联网企业工程师才能拿到。也许是的,小公司或者非互联网企业拿二万的不太可…

Threejs进阶之十四:在uniapp中使用threejs创建三维图形

在uniapp中使用threejs 一、uni-app介绍二、新建uni-app项目三、安装three.js库四、在vue组件中引入three.js库五、创建场景(Scene)和相机(Camera)六、创建渲染器(Renderer)七、创建物体和灯光八、渲染场景(Scene)九、运行测试核心代码 一、uni-app介绍 uni-app是一个基于Vue.…

sqlserver 中的表值函数和标量函数

目录 一、表值函数 1.内联表值函数 1.创建函数 2.调用函数 3.返回结果 2.多语句的表值函数 2.调用函数 3.返回结果 3.内联表值函数和多语句的表值函数的区别 1.语法上 2.结构上 二、标量函数 1.创建函数 2.调用函数 2.返回结果 总结 一、表值函数 表值函数是返回一个Table类型…

如何使用jmeter进行压测

1.概述 一款工具,功能往往是很多的,细枝末节的地方也很多,实际的测试工作中,绝大多数场景会用到的也就是一些核心功能,根本不需要我们事无巨细的去掌握工具的所有功能。所以本文将用带价最小的方式讲解如何快速上手使用…

centos7.5离线安装部署TiDB-6.5.0分布式系统

centos7.5离线安装部署TiDB-6.5.0分布式系统 一、需求,为什么要部署TiDB-6.5.0分布式系统 当前绝大部分企业的业务数据都分散在不同的系统中,没有一个统一的汇总,随着业务的发展,企业的决策层需要了解整个公司的业务状况以便及时…

【Linux Network】应用层协议——HTTP

目录 1. 认识URL 2. urlencode和urldecode urlencode例子: urldecode例子: 3. HTTP协议格式 3.1 HTTP请求: 3.2 HTTP响应: 3.3 HTTP的方法: 3.4 GET方法和POST方法的区别 3.5 HTTP的状态码: 3.6 HTTP常见He…

Buf 教程 - 使用 Protobuf 生成 Golang 代码和 Typescript 类型定义

简介 Buf 是一款更高效、开发者友好的 Protobuf API 管理工具,不仅支持代码生成,还支持插件和 Protobuf 格式化。 我们可以使用 Buf 替代原本基于 Protoc 的代码生成流程,一方面可以统一管理团队 Protoc 插件的版本、代码生成配置&#xff…

QT的qrc文件的创建和编辑

qrc文件,这个是Qt的资源文件,如果在pro文件中不包含的话,在编译的时候会提示找不到相应资源的错误;下面说一下手动修改pro和编写qrc文件的方法: 2.1 添加qrc文件; 2.2 编写qrc文件; 可以用 file…

Linux_证书_Openssl工具详解

文章目录 OpenSSLopenssl实现对称加密openssl生成密钥对、非对称加密、数字签名根据CA颁布证书生成ca私钥和ca证书根据ca生成证书 小结 OpenSSL OpenSSL 是一个开源项目,其组成主要包括一下三个组件: openssl:多用途的命令行工具 libcrypt…

AR VR 到底哪种技术可以改变未来?

随着科技的不断进步,虚拟现实(VR)和增强现实(AR)技术已经成为了当今科技领域的热门话题。VR和AR的出现,为人们带来了前所未有的体验和感受,也为各行各业的发展提供了新的机遇。但是,…

clang-format configurator - 交互式创建 clang-format 格式配置文件

clang-format configurator - 交互式创建 clang-format 格式配置文件 clang-format configurator https://zed0.co.uk/clang-format-configurator/ clang-format-configurator https://github.com/zed0/clang-format-configurator Interactively create a clang-format confi…