LeetCode刷题---链表经典问题(双指针)

news2024/11/16 18:42:06

文章目录

  • 一、编程题:206. 反转链表(双指针-头插法)
  • 解题思路
      • 1.思路
      • 2.复杂度分析:
      • 3.算法图解
  • 代码实现
  • 二、编程题:203. 移除链表元素
  • 解题思路
      • 1.思路
      • 2.复杂度分析:
      • 3.算法图解
  • 代码实现
  • 三、编程题:328. 奇偶链表(双指针)
  • 解题思路
      • 1.思路
      • 2.复杂度分析:
      • 3.算法图解
  • 代码实现
  • 四、编程题:234. 回文链表(双指针-快慢指针)
  • 解题思路
      • 1.思路
      • 2.复杂度分析:
      • 3.算法图解
  • 代码实现
  • 总结


一、编程题:206. 反转链表(双指针-头插法)

1.题目描述

  给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 LeetCode题目链接。

2.示例1:

在这里插入图片描述

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

3.示例2:

在这里插入图片描述

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

4.示例3:

输入:head = []
输出:[]

5.提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

6.进阶:

  • 链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

解题思路

  可以根据滑动窗口的思想来解决本题;
  这题采用双指针,本题主要关键点为:

  • 1.该怎么去移动这两个指针?这一点要理清楚。这里双指针移动就比较简单了,分别向下移动一步即可;

1.思路

解决方法1(个人想法):

  • Step 1.创建指针last指向head,和临时结点temp;
  • Step 2.对原链表进行遍历,当last的下一个结点不为空时,则将该结点插入到头结点面前并更新头结点;
  • Step 3.重复Step2直到遍历结束,此时头结点就是反转后的链表;

看不懂解释的话,直接看算法图解比较容易理解点

2.复杂度分析:

时间复杂度:O(L),其中L是链表的长度。
空间复杂度:O(1)

3.算法图解

灰色部分代表头指针head(注:本人不会做成流程动画,希望会的朋友可以私信我指点一二,说个软件名字也可以,谢谢

请添加图片描述


代码实现

每个代码块都写了注释,方便理解,代码还可以改进;

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode temp, last = head;
        while(last!=null){
            if(last.next != null){
                temp = last.next;
                last.next = temp.next;
                temp.next = head;
                head = temp;
            }else break;
        }
        return head;
    }
}

提交结果:

在这里插入图片描述


二、编程题:203. 移除链表元素

1.题目描述

  给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。 LeetCode题目链接。

2.示例1:

在这里插入图片描述

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

3.示例2:

输入:head = [], val = 1
输出:[]

4.示例3:

输入:head = [7,7,7,7], val = 7
输出:[]

5.提示:

  • 列表中的节点数目在范围 [0, 104] 内
  • 1 <= Node.val <= 50
  • 0 <= val <= 50

解题思路

  这题比较简单,只要遍历链表找到相同值的前驱节点进行操作即可。这里对第一个节点的处理可分为两种:是否带有头节点;

1.思路

解决方法1(个人想法):

1、不带头结点

  • Step 1.当head节点为null可直接返回,创建temp来遍历链表;
  • Step 2.可以先对第一个节点不做处理,从第二个节点开始进行处理,遍历链表遇到相同的值进行删除即可;
  • Step 3.直到遍历完之后再来处理第一个结点,看是否需要删除;

2、带头结点

  • Step 1.与前面的思路同理,只需要创建一个头结点来指向链表,这样链表中的结点就可以从第二个结点开始处理了;

看不懂解释的话,直接看算法图解比较容易理解点

2.复杂度分析:

时间复杂度:O(L),其中L是链表的长度。
空间复杂度:O(1)

3.算法图解

红色部分代表删除的节点,灰色部分代表头节点(不带虚拟头节点)(注:本人不会做成流程动画,希望会的朋友可以私信我指点一二,说个软件名字也可以,谢谢

请添加图片描述


代码实现

每个代码块都写了注释,方便理解,代码还可以改进;

不带头结点

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        // 不带头结点
        if(head == null) return head;
        ListNode temp = head;
        while(temp.next != null){
            if(temp.next.val == val)
                temp.next = temp.next.next;
            else temp = temp.next;
        }
        return head.val == val ? head.next : head;
    }
}

带头结点

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        // 带头结点解决
        ListNode first = new ListNode(0, head);
        ListNode temp = first;

        while(temp.next != null){
            if(temp.next.val == val)
                temp.next = temp.next.next;
            else temp = temp.next;
        }
        return first.next;
    }
}

提交结果:
在这里插入图片描述


三、编程题:328. 奇偶链表(双指针)

1.题目描述

  给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。LeetCode题目链接。

2.示例1:

在这里插入图片描述

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

3.示例2:

在这里插入图片描述

输入: head = [2,1,3,5,6,4,7]
输出: [2,3,6,7,1,5,4]

5.提示:

  • n == 链表中的节点数
  • 0 <= n <= 104
  • -106 <= Node.val <= 106

解题思路

  这题可以将奇数节点和偶数节点抽离为奇数链表和偶数链表,然后将偶数链表合并在奇数链表即可;
  这题采用双指针,本题主要关键点为:

  • first指针负责指向奇数节点,second指针偶数节点,然后在遍历过程中维护两个指针即可;

1.思路

解决方法1(个人想法):

  • Step 1.创建一个偶数链表的头节点,first指向原链表的头节点(奇数节点),second指向偶数链表的头节点;
  • Step 2.对原链表进行遍历,每一步首先更新偶数节点,然后在更新奇数节点;
  • Step 3.重复step 2的操作,直到全部节点分离完为止,遍历结束的条件为fisrt.next == null和second.next == null,也就是偶数节点为空或者下一个奇数节点为空时结束循环,;
  • Step 4.此时first指向最后一个奇数节点,second指向最后一个偶数节点,最后将fisrt.next指向偶数链表头节点的后继节点temp.next即可;

看不懂解释的话,直接看算法图解比较容易理解点

2.复杂度分析:

时间复杂度:O(L),其中 L 是链表的节点数。需要遍历链表中的每个节点,并更新指针。
空间复杂度:O(1)

3.算法图解

红色部分代表偶数链表,蓝色部分代表奇数链表(注:本人不会做成流程动画,希望会的朋友可以私信我指点一二,说个软件名字也可以,谢谢

请添加图片描述


代码实现

每个代码块都写了注释,方便理解,代码还可以改进;

class Solution {
    public ListNode oddEvenList(ListNode head) {
        if(head == null) return head;
        ListNode temp = new ListNode(0);
        // ListNode first = new ListNode(0, head);
        ListNode first = head, second = temp;

        while(first != null){
            // 对当前偶数链表进行添加操作
            second.next = first.next;
            // 切换到偶数链表的最后一个节点
            second = second.next;
            // 判断偶数节点和下一个奇数节点是否存在
            if(first.next != null && second.next != null){
                // 将奇数节点进行链接
                first.next = second.next;
                first = first.next;
            }else break;
        }
        first.next = temp.next;
        return head;
    }
}

提交结果:

在这里插入图片描述

四、编程题:234. 回文链表(双指针-快慢指针)

1.题目描述

  给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。LeetCode题目链接。

2.示例1:

在这里插入图片描述

输入:head = [1,2,2,1]
输出:true

3.示例2:

在这里插入图片描述

输入:head = [1,2]
输出:false

5.提示:

  • 链表中节点数目在范围[1, 105] 内
  • 0 <= Node.val <= 9

6.进阶:

你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?


解题思路

  这题采用快慢指针和反转链表来进行解决;
  这题采用双指针,本题主要关键点为:

  • 1.该怎么去移动这两个指针?这一点要理清楚。这里双指针移动就比较简单了,分别向下移动一步即可;
  • 2.在调用next字段之前,始终检查节点是否为空。获取空节点的下一个节点将导致空指针错误。例如,在我们运行fast = fast.next.next之前,需要检查fast和fast.next不为空;
  • 3.对链表的后半部分进行反转,然后将前半跟后半部分进行比较,结束之后恢复并返回结果;

1.思路

解决方法1(个人想法):

  • Step 1.创建指针fast和slow均指向head,在运行过程中,slow每次向后走一步,fast每次向后走两步,当快指针fast移动到链表的末尾时,慢指针slow恰好到链表的中间;
  • Step 2.可以使用206. 反转链表问题中的解决方法来反转链表的后半部分;
  • Step 3.比较链表前后两个部分的值可得出结果,当后半部分到达末尾则比较完成;
  • Step 4.比较结束之后,通过Step2的方法对链表后半部分进行反转使其恢复原状;

看不懂解释的话,直接看算法图解比较容易理解点

2.复杂度分析:

时间复杂度:O(n),其中 n 指的是链表的大小。
空间复杂度:O(1)。我们只会修改原本链表中节点的指向,而在堆栈上的堆栈帧不超过 O(1)。

3.算法图解

快慢指针找中间节点的前驱节点(红色部分代表快指针,灰色部分代表慢指针)(注:本人不会做成流程动画,希望会的朋友可以私信我指点一二,说个软件名字也可以,谢谢

请添加图片描述

反转后半链表

注:下面代码中第一次反转并没有将中间节点的前驱节点与反转后头节点进行连接,也就是打印出来是1-2-2,而不是1-2-1-2

请添加图片描述


代码实现

每个代码块都写了注释,方便理解,代码还可以改进;

class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head.next == null) return true;
        boolean result = true;
        ListNode fast = head, slow = head, even;
        // 要找中间节点的前驱节点
        slow = searchMiddle(head);

        // 根据中间结点反转后边的链表相与前面的链表进行对比
        even = reverseList(slow.next);
        fast = head;

        while(even != null){
            if(fast.val != even.val) result = false;
            even = even.next;
            fast = fast.next;
        }

        // 恢复链表的可以在将后半在反转一次
        slow.next = reverseList(slow.next);

        return result;
    }

    private ListNode searchMiddle(ListNode head) {
        // 先用快慢指针找到链表中间节点的前驱节点
        ListNode fast = head;
        ListNode slow = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }

    private ListNode reverseList(ListNode head){
        ListNode temp = null;
        ListNode curr = head;

        while (curr.next != null) {
            temp = curr.next;
            curr.next = temp.next;
            temp.next = head;
            head = temp;
        }

        return head;
    }
}

提交结果:

在这里插入图片描述


总结

1、可以同时使用多个指针。
有时,当你为链表问题设计算法时,可能需要同时跟踪多个结点。您应该记住需要跟踪哪些结点,并且可以自由地使用几个不同的结点指针来同时跟踪这些结点。

2、在许多情况下,你需要跟踪当前结点的前一个结点。
你无法追溯单链表中的前一个结点。因此,您不仅要存储当前结点,还要存储前一个结点。这在双链表中是不同的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式开发:为什么物联网正在吞噬嵌入式操作系统?

在过去几年的嵌入式开发中&#xff0c;独立嵌入式软件市场的两大基石已被物联网公司完全吞噬。第一个FreeRTOS被亚马逊吞并&#xff0c;以支持其亚马逊Web服务(AWS)云平台的物联网开发&#xff0c;Express Logic被微软吞并&#xff0c;用于其竞争对手Azure云服务。许多分析师对…

【图像处理OpenCV(C++版)】——4.4 对比度增强之伽马变换

前言&#xff1a; &#x1f60a;&#x1f60a;&#x1f60a;欢迎来到本博客&#x1f60a;&#x1f60a;&#x1f60a; &#x1f31f;&#x1f31f;&#x1f31f; 本专栏主要结合OpenCV和C来实现一些基本的图像处理算法并详细解释各参数含义&#xff0c;适用于平时学习、工作快…

解析某音X-Bogus参数

文章目录写在前面目标正向梳理主要加密函数主要算法解析逆向梳理结论测试进阶写在后面写在前面 本文主要介绍从X-Bogus还原19位数组的过程&#xff0c;方便理解算法。 目标 // 从 var x_bogus DFSzswVVUyXANrqJSkdAml9WX7jG; // 还原出 var x_array [64, 0.00390625, 1, 2…

Dubbo 入门系列之快速部署一个微服务应用

本文将基于 Dubbo Samples 示例演示如何快速搭建并部署一个微服务应用。 背景 Dubbo 作为一款微服务框架&#xff0c;最重要的是向用户提供跨进程的 RPC 远程调用能力。如上图所示&#xff0c;Dubbo 的服务消费者&#xff08;Consumer&#xff09;通过一系列的工作将请求发送给…

FatFs文件系统(只针对SPI-Flash)总结

作用 当我们利用SPI操作Flash时往往读写的都是一段连续的扇区&#xff0c;而FatFs文件系统可以将我们要写入的数据拆分成不连续的扇区见缝插针写入&#xff0c;类似与链表一块扇区指向下一块扇区&#xff0c;不需要物理逻辑地址连续也可以读取整个文件。 这是为啥嘞&#xff…

从零开始部署“生产级别”的主从模式Mysql

从零开始部署“生产级别”的主从模式Mysql 1. 撰写的缘由 Mysql 在日常应用中使用范围非常广泛&#xff0c;它的部署&#xff0c;其实一个docker run 就可以搞定了&#xff0c;但是这种单个standalone模式下&#xff0c;非常不具备高可用性。 测试环境和开发可以随便用&…

集团审计BI项目的特点

审计到底做哪些事情呢&#xff1f;如果之前大家没有接触的话&#xff0c;试着想一下&#xff0c;可能都会想到审计工作做的应该是跟监督有关的事情。实际上&#xff0c;现代审计职责不仅仅只是监督&#xff0c;还要兼顾到服务&#xff0c;具有监督和服务的双重属性。 什么是审…

stm32学习笔记-2 软件安装及创建工程

2 软件安装及创建工程 [toc] 注&#xff1a;笔记主要参考B站 江科大自化协 教学视频“STM32入门教程-2023持续更新中”。 注&#xff1a;工程及代码文件放在了本人的Github仓库。 2.1 软件安装 软件安装的步骤有&#xff1a; 安装Keil5 MDK。Keil5 MDK专门用于给ARM系列单片…

网络安全规划实践

在企业IT战略规划方面&#xff0c;很多时候我们会自动忽略网络安全规划&#xff0c;一是不够重视&#xff0c;从公司到技术部门&#xff0c;对网络安全的认识有限&#xff0c;重视不够&#xff0c;不愿意花钱。 二是技术部门自身原因&#xff0c;不愿意多花成本和精力去规划&am…

推荐14款最受欢迎的3d建模软件

最好的 3D 建模软件可以毫不费力地设计出最奇特的创意&#xff0c;并将它们变成令人惊叹的 3D 可视化效果。如果您确切知道要设计的模型类型&#xff0c;请查看此 3D 建模软件列表&#xff0c;比较 15 种一流的 3D 建模平台&#xff0c;然后选择最适合您的一款。最佳 3D 建模软…

金兔迎福报、新春第一炮【2022 中国开源年度报告】!

【中国开源年度报告】由开源社从 2015 年发起&#xff0c;是国内首个结合多个开源社区、高校、媒体、风投、企业与个人&#xff0c;以纯志愿、非营利的理念和开源社区协作的模式&#xff0c;携手共创完成的开源研究报告。后来由于一些因素暂停&#xff0c;在 2018 年重启了这个…

【王道数据结构】第一章 | 绪论 | 数据结构与算法的概念

目录 1.1数据结构的基本概念 1.2数据结构的三要素 1&#xff09;.数据的逻辑结构&#xff1a; 2&#xff09;.数据的存储结构&#xff08;物理结构&#xff09;&#xff1a; 3&#xff09;.数据的运算 4&#xff09;.数据类型和抽线数据类型 1.3算法的基本概念 1.4 空间…

人大金仓数据库分区表

分区表 声明式创建分区 按列创建分区&#xff08;PARTITION BY LIST&#xff09; 将学员表student按所在城市使用partition by list创建分区 创建分区表&#xff08;基表&#xff09; 创建格式 create table 表名&#xff08;字段名 数据类型&#xff09;PARTITION BY LI…

Redis哨兵工作原理 | 黑马Redis高级篇

哨兵的作用 Redis提供了哨兵机制来实现主从集群的自动故障恢复 监控&#xff1a;sentinel会不断检查master和slave是否按照预期工作 自动故障恢复&#xff1a;如果master故障&#xff0c;sentinel会将一个slave变为master&#xff0c;当故障实例恢复后也以新的master为主 通…

低代码平台助力交通行业数字化科学管理

编者按&#xff1a;本文分析了交通行业的数字化转型需求&#xff0c;并指出了适合交通行业的低代码平台的特性&#xff0c;最后通过相关案例进行了功能展示。关键词&#xff1a;对接能力&#xff0c;国产化&#xff0c;数据引擎&#xff0c;智能化交通运输是国民经济先导性、战…

3、基本的SELECT语句

文章目录1. SQL概述1.1 SQL背景知识1.2 SQL语言排行榜1.3 SQL 分类2 SQL语言的规则与规范2.1 基本规则2.2 SQL大小写规范 &#xff08;建议遵守&#xff09;2.3 注 释2.4 命名规则&#xff08;暂时了解&#xff09;2.5 数据导入指令3 基本的SELECT语句3.0 SELECT...3.1 SELECT …

大数据技术架构(组件)15——Hive:内置UDAF函数

1.4.10、内置UDAF函数1.4.10.1、count--可以发现count(id)会把idnull的值剔除掉select count(1),count(*),count(distinct id),count(id) from test1.4.10.2、sumselect sum(1) from test;1.4.10.3、avg该函数太简单了&#xff0c;就不给大家演示了1.4.10.4、min该函数太简单了…

Hive(5):数据定义语言(DDL)

1 数据定义语言&#xff08;DDL&#xff09;概述 1.1 DDL语法的作用 数据定义语言 (Data Definition Language, DDL)&#xff0c;是SQL语言集中对数据库内部的对象结构进行创建&#xff0c;删除&#xff0c;修改等的操作语言&#xff0c;这些数据库对象包括database&#xff…

面试官问 ,Mybatis SELECT 查询, 集合或者单个对象,如果数据库不存在数据,需要判空吗?

前言 于昨日下班时段&#xff0c;本人正在与生活作斗争&#xff0c;收到了金三银四一线作战小队成员紧急反应的战况问题。 不熟悉的或者是不知道怎么去看源码的看官&#xff0c;上车了。 正文 这面试题问的&#xff0c; 考察的是什么&#xff1f; ① mybatis框架的应用掌握情…

如何实现报表集成?(三)——资源集成

在上一篇&#xff0c;我们介绍了用户同步和单点登录&#xff0c;帮助用户了解什么是用户同步、如何做用户验证&#xff0c;以及如何实现单点登录。 这一篇&#xff0c;我们看下如何做资源集成。行文过程中得到了来自报表软件厂商 Smartbi 的报表产品&#xff1a;电子表格软件的…