图解链表反转系列Leetcode

单链表反转两种方法 最近同学介绍了一个lettcode(力扣)OJ给我,个人认为这个网站比母校的oj,杭电oj界面友好很多,题库充足,且支持多种主流语言,很适合闲时刷刷提高算法能力,算法的练习如同内功的修炼,碰到算法问题,经常有一种无力感,只能慢慢总结了。 链表是一种重要的数据结构,因为有递归性质,所以总是难以理解,涉及链表的复杂操作总是感觉一头雾水,看别人的实现代码总是似懂非懂,看完就忘,实际上就是没有理解透彻,特... 阅读详情

前言

链表反转是基础数据结构链表的常见操作之一,链表的反转能够产生其他的拓展题目,以下将以LC206 反转链表、LC24 两两交换链表中的节点、LC25 K个一组翻转链表为例进行分析讲解。

LC 206 反转链表

在这里插入图片描述
对于本题,我们通过以下图解来说明。
首先,以下列链表为例:
在这里插入图片描述
这里我们创建一个节点(为null),使用指针pre指向它(作为当前节点的前驱节点),使用指针cur指向当前节点(此时为head节点),再用一个指针node指向当前节点的后继节点。
对于当前节点cur,我们执行以下操作:

  • 首先记录当前节点cur的后继节点; node = cur.next
  • 其次修改当前节点cur的后继节点为pre; cur.next = pre

对于第一个节点来说,当链表全部反转后,它将是最后一个节点,它的后继节点指向null。此时,我们完成了第一个节点的反转。

在这里插入图片描述
对第一个节点操作完成后,我们对第二个节点进行操作。
此时cur指向节点2,也就是上一次操作中的node节点; cur = node
pre指向节点1,也即上一次操作中的cur节点; pre = cur
对于当前节点cur,我们执行同样的操作:

  • 首先记录当前节点cur的后继节点; node = cur.next
  • 其次修改当前节点cur的后继节点为pre; cur.next = pre

此时我们完成了节点2的反转操作。
在这里插入图片描述
以此类推,此时对节点3进行反转:
在这里插入图片描述
此时对节点4进行反转:
在这里插入图片描述
此时对节点5进行反转:
这里注意在原链表中,节点5是最后一个节点,其后继节点为null,当我们依次操作每个节点,直到当前节点为原链表最后一个节点时,对于当前节点cur,我们执行同样的操作:

  • 首先记录当前节点cur的后继节点; node = cur.next
  • 其次修改当前节点cur的后继节点为pre; cur.next = pre

操作完毕,我们可以通过判断node是否为null决定是否返回反转后的链表。
在这里插入图片描述
通过以上的步骤,我们实现了对普通链表的反转操作,代码如下

public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null){
            ListNode node = cur.next;
            cur.next = pre;
            if(node == null){
                return cur;
            }
            pre = cur;
            cur = node;
        }
        return cur; 
    }

LC 24 两两交换链表中的节点

在这里插入图片描述
本题是链表反转的拓展版,在简单的链表反转中,我们只需要反转一个链表即可。而在本题中,我们相当于需要反转length / 2个短的链表,每个短链表含有两个节点。(length是链表的长度)

接下来,我们继续进行图解:(依然以上述链表为例)
首先我们对第一个短链表1,2进行反转。同样的,定义一个节点-1,使用指针pre指向它(这里我们可以看到与LC206的区别,LC206的初始pre指向null,因为反转后的链表只有一个,最后一个节点的后继一定为null;而本题的pre指向一个初始化的节点,这是因为对于分段链表,每一段的pre应指向前一段的最后一个节点,而该节点为非空节点),使用指针cur指向当前节点(这里为head节点),使用指针node存储当前节点的后继节点。
在这里插入图片描述
对第一个短链表进行反转,反转后的短链表应为2,1。对当前节点1操作完毕,下一次将对节点3进行操作,那么显而易见的,当前节点cur的后继节点应为操作节点3。而pre节点的后继节点应为反转后短链表的头节点2。
对于当前节点cur执行以下操作:

  • 首先记录当前节点的后继节点; node = cur.next
  • 其次令当前节点的后继节点为node的后继节点; cur.next = node.next
  • 然后令node节点的后继节点为当前节点; node.next = cur
  • 最后令pre节点的后继节点为node节点; pre.next = node;

在这里插入图片描述
操作完毕,得到链表为:
在这里插入图片描述
第一段小的链表反转完毕,即将操作第二段链表,cur指针指向节点3,也即上一步中的cur.next,pre指针指向节点1,也即上一步中node节点的后继节点。
在这里插入图片描述
对第一二短链表进行反转,反转后的短链表应为4,3。对当前节点3操作完毕,下一次将对节点5进行操作,当前节点cur的后继节点应为操作节点5。而pre节点的后继节点应为反转后短链表的头节点4。
对于当前节点cur执行以下操作:

  • 首先记录当前节点的后继节点; node = cur.next
  • 其次令当前节点的后继节点为node的后继节点; cur.next = node.next
  • 然后令node节点的后继节点为当前节点; node.next = cur
  • 最后令pre节点的后继节点为node节点; pre.next = node;

在这里插入图片描述
操作完毕,得到链表为:
在这里插入图片描述
第二段小的链表反转完毕,即将操作第三段链表,cur指针指向节点5,也即上一步中的cur.next,pre指针指向节点3,也即上一步中node节点的后继节点。在第三段短链表中,我们可以看到,该段链表只有一个节点,我们将该段短链表视为5, null。该段链表不需要反转,可以得到链表遍历到最后的出口为cur.next为null。
在这里插入图片描述
通过以上的步骤,我们实现了对两两节点的反转操作,代码如下

public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null){
            return head;
        }
        ListNode cur = head;
        ListNode pre = new ListNode(-1);
        pre.next = cur;
        ListNode res = head.next;
        while(cur != null && cur.next != null){
            ListNode node = cur.next;
            cur.next = node.next;
            node.next = cur;
            pre.next = node;
            cur = cur.next;
            pre = node.next;
        }
        return res; 
    }

LC25 K个一组翻转链表

在这里插入图片描述
本题是前两道题的综合版,前一道题目两两反转,意味着反转多组短链表,每个链表的长度为2;本题则是反转多组链表,每个链表的长度为k(简单链表反转可以看作反转多组链表,每个链表的长度为1)。自然的,我们的思路为,对于每一组短链表,使用反转简单链表的函数reverse来进行翻转,翻转后将多段短链表连接起来。这里的重点问题是: 如何连接已翻转短链表和未翻转链表?
回顾之前的两道题目,我们定义了指针cur, pre来记录操作节点与操作节点的前驱节点,这里我们同样定义cur和pre,pre指向新创建的节点-1。

在第一个题目中,我们可以看到,cur节点与pre节点的距离为1;且遍历每一个节点时,pre与cur都向右移动一个距离;(pre指向当前短链表的末尾,cur指向下一个短链表的头部)
在这里插入图片描述
在这里插入图片描述
在第二个题目中,我们可以看到,cur节点与pre节点的距离同样为1;而遍历每一个节点时,pre与cur都向右移动两个距离;(pre指向当前短链表的末尾,cur指向下一个短链表的头部)在这里插入图片描述
在这里插入图片描述

那么当短链表长度为k时,cur节点与pre节点的距离为1;遍历每一个节点时,pre与cur都向右移动k个距离,pre指向当前短链表的末尾,cur指向下一个短链表的头部,从而完成k个一组链表的翻转。(这里我们使用pHead记录当前短链表的头节点,nextPre记录下一个短链表的前驱节点,也即当前列表操作后的末尾节点,nextPHead记录下一个短链表的头节点)
顺着这个思路,我们以下列链表为例,令k=3, 图解其翻转过程:
在这里插入图片描述
首先,我们创建新节点-1,指针pre指向节点-1。初始化pHead和nextPre为头节点。对第一段短链表进行操作:

  • 首先遍历第一段链表,找到nextPHead;

在这里插入图片描述

  • 其次对第一段链表进行翻转;
  • 令该段链表的前驱节点pre的后继指向反转后链表的头。 pre.next = reverse(pHead, k)

在这里插入图片描述
得到链表为:
在这里插入图片描述

对第一段链表操作完毕,接下来操作第二段链表。当前段链表的初始头节点pHead为节点4,也即上一次操作中的nextPHead节点;当前端链表的前驱节点pre为节点1,也即上一次操作中的nextPre节点。对于下一段链表,其前驱节点nextPre为本段链表的头节点4,遍历第二段链表,找到nextPHead。
在这里插入图片描述
同样地,

  • 对第二段链表进行翻转;
  • 令该段链表的前驱节点pre的后继指向反转后链表的头。 pre.next = reverse(pHead, k)

在这里插入图片描述
得到链表为:
在这里插入图片描述
对第二段链表操作完毕,操作第三段链表:
在这里插入图片描述
第三段链表长度不足k=3, 所以直接令前驱节点pre的后继节点为当前段链表的头pHead,无需翻转操作。
在这里插入图片描述
通过以上的步骤,我们实现了对K个一组链表的翻转操作,代码如下

public ListNode reverseKGroup(ListNode head, int k) {
        int count = 0;
        ListNode pre = new ListNode(-1);
        pre.next = head;
        ListNode res = pre;
        ListNode pHead = head;
        ListNode nextPHead = head;
        ListNode nextPre = head;
        int len = length(head);
        while(count + k <= len){
            for(int i = 0; i < k; i++){
                count++;
                nextPHead = nextPHead.next;
            }
            
            ListNode partHead = reverse(pHead, k);
            pre.next = partHead;
            pre = nextPre;
            nextPre = nextPHead;
            pHead = nextPHead;   
        }
        if(pHead != null){
            pre.next = pHead;
        }
        return res.next;    
    }

    public ListNode reverse(ListNode head, int k){
        // return the head of the reverse list
        ListNode pre = null;
        ListNode cur = head;
        int count = 0;
        while(count < k){
            ListNode node = cur.next;
            cur.next = pre;
            pre = cur;
            if(count == k - 1){
                return cur;
            }
            cur = node;
            count++;

        }
        return cur;
    }

    public int length(ListNode head){
        if(head == null){
            return 0;
        }
        int len = 0;
        while(head != null){
            len++;
            head = head.next;
        }
        return len;
    }

总结

以上题目之间存在层层递进关系,逐步分析,能够加深对链表反转的理解。

【实战】ACM 选手图解 LeetCode 反转链表 图解 LeetCode 206:反转链表 阅读详情

相关推荐

别再怕DRIVER_VERIFIER_DMA_VIOLATION蓝屏:手把手教你用WinDbg分析IOMMU与驱动冲突

本文深入解析DRIVER_VERIFIER_DMA_VIOLATION蓝屏错误,从IOMMU原理到实战排错,手把手教你使用WinDbg分析驱动冲突。通过理解DMA与IOMMU的核心机制、搭建分析环境、解读DMAR表与设备拓扑,最终定位问题驱动并提供解决方案,帮助开发者和IT运维人员有效应对此类系统崩溃问题。

weixin_30298497的博客 803

图解:单链表反转的三种方式!

当我们在聊到链表反转的时候,一定说的都是单链表,双链表本身就具有前驱指针 Prev 和后续指针 next,无需进行翻转。 单链表反转,反转后的效果如下: 看起来很简单,只需要将单链表所有结点的 next 指向,指向它的前驱节点即可。引入一个栈结构,就可以实现。 栈实现的链表反转 在原本链表的数据结构之外,引入一个栈(数组也可),将单链表循环遍历,将所有结点入栈,最后再从栈中循环出栈,记住出栈的...

Android 开发架构 4087

【ES】--Elasticsearch的分词器深度研究

分词器是ik_max_word,会将“龙马家书”拆分为“龙马”、“马家”、“家书”。然后filter配置的是edge_ngram_filter,在上面拆分的token词组基础上,按照edge_ngram_filter规则进一步拆分。从结果看:分词器是keyword,filter支持edge_ngram_filter,字符过滤器支持tsconvert【简繁体】。分词器是keyword,表明是不进行分词,但filtet中edge_ngram_filter是按照布长方式拆分token,所以得到如此结果。

寻梦友的博客 2435

图解反转链表的4种方法

NO.1 剑指 Offer 24. 反转链表 (简单) 定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 限制: 0 <= 节点个数 <= 5000 链接:https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/ 方法一: 遍历原链表,把“当前结点

IAMLSL的博客 1768

成分数据的闭合效应对地球化学数据的影响及其解决方案

封闭效应 (Closure Effect),也称为常数和效应 (Constant Sum Effect),是地球化学数据分析中一个需要特别注意的统计现象。它的产生是由于我们通常使用成分数据 (Compositional Data)百分比 (%)百万分率 (ppm)由于这些数据表示的是各组分占总体的比例,它们的总和必然是一个常数 (例如 100% 或 1,000,000 ppm)。这意味着每个元素的含量不是独立的,而是与其他元素的含量相互制约。改变其中一个元素的含量必然会影响其他元素的相对比例。

qq_59349063的博客 1538

实战分享:如何用YOLOv5s+BIFPN高效检测隧道裂缝(附数据集获取)

本文详细介绍了如何利用YOLOv5s结合BIFPN(双向特征金字塔网络)构建高效的隧道裂缝智能检测系统。文章从高质量数据集构建、针对性数据增强、模型架构选型与调优,到工程化部署全链路进行了实战分享,旨在为基础设施自动化巡检提供兼顾精度与速度的解决方案。

weixin_29062823的博客 380

LeetCode 第24题两两交换链表中的节点swapPairs

LeetCode 第24题两两交换链表中的节点swapPairs

weixin_41221502的博客 491

傻白入门芯片设计,wafer/die/chip/cell(一)

芯片设计的一些基本概念

qq_46675545的博客 2万+

反转链表(递归法)图文详解

通过“先深入后返回”的策略,首先会一直递归调用自身,直到到达链表的尾部,并在递归的过程中改变节点之间的指向来实现链表的反转。具体步骤是先递归地反转子链表,然后将当前节点的下一个节点的指针指向当前节点,并将当前节点的指针指向空。具体步骤是使用三个指针分别指向当前节点、前一个节点和下一个节点,然后在遍历过程中不断更新这三个指针的指向,直到遍历到链表末尾。,意思 4 这个结点指向空结点,不再指向任何结点了,当然包括 5 这个结点。的知识去理解,搞清楚到底函数调用时,原本的参数值是什么。

记录学习与成长! 1776

NFC门禁卡复制全解析:从M1卡到UID卡的攻防实践

本文深入解析了NFC门禁卡的安全机制与攻防实践。从M1卡基于Crypto1算法的原理漏洞讲起,详细阐述了利用Proxmark3等工具进行侦听、破解密钥及使用UID卡进行复制的完整攻击链条。同时,为防御方提供了从升级CPU卡、启用全扇区认证到部署多因子验证的系统性防护策略,旨在帮助管理员构建更稳固的物理安全防线。

weixin_29182481的博客 1401

告别偏移地址!用C# S7PLUS驱动读写西门子S7-1500符号地址(TIA Portal V17+环境配置)

本文详细介绍了如何在TIA Portal V17+环境中使用C# S7PLUS驱动读写西门子S7-1500 PLC的符号地址,告别传统的偏移地址方式。通过配置OpenSSL、集成S7PLUS驱动库,以及实战案例展示符号地址的读写方法,显著提升代码可读性和维护效率。文章还涵盖了环境准备、高级配置及常见问题解决方案,助力工业自动化开发者实现更高效的PLC通讯。

weixin_28720767的博客 317

Solution001两个一组翻转链表

给你一个链表,让你两个两个的将其翻转! 这题其实就是K个一组翻转链表的特例,这里想写一下的目的是,虽然常规做法也是递归,那么我可不可以完全使用递归的方式做一下 方法一:常规做法,先翻转K个链表,再递归 public static ListNode func2(ListNode head){ if (head==null ||head.next==null) return head; ListNode tail = head; for (int i=1;i&.

Oblak_ZY的博客 804

uniapp:实现扫描二维码+自定义扫描框+扫一扫功能,支持扫码识别二维码、条形码(条码)、微信/支付宝等二维码功能,自定义扫码页面可添加按钮或文字展示布局(移动端H5网页/小程序/安卓苹果APP端)

UNI-APP实现扫描二维码,uniapp扫描条形码,uniapp实现扫一扫功能,扫码成功后跳转页面,uniApp扫描条码,uniApp-H5 使用摄像头扫码功能(扫一扫),实现手机扫描二维码并跳转全过程示例代码,用户扫码后自动跳转到链接,uniapp怎么调用扫一扫功能,uniapp实现扫一扫功能,uniapp请求相机使用权限,扫一扫权限申请,uniapp调用摄像头扫码,uniapp实现调用相机扫码,UniApp 实现扫描二维码,uniapp APP安卓苹果ios实现扫码扫一扫,Uniapp微信小程序扫描二

✅ 精选推荐文章 4708

LeetCode反转链表java_Leetcode 反转链表系列 图解详细过程

对于一个程序猿来说,数据结构和算法的重要性就不用我多说了吧,算法题已然成了现在大厂笔试面试的重头戏,废话少说,Leetcode 刷起来呀。说起刷 Leetcode,我建议你按 tag 刷,不然只能像无头苍蝇,东一榔头西一棒槌,最后事倍功半 (过来人的惨痛经历)。最近正好在刷 Leetcode 上的链表题,也碰到了一些颇具代表性的题型,正好做个记录,也分享给有需要的小伙伴。对链表不太熟悉的小伙伴碰到...

weixin_33444829的博客 580

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Leetcode 反转链表系列 图解详细过程

对于一个程序猿来说,数据结构和算法的重要性就不用我多说了吧,算法题已然成了现在大厂笔试面试的重头戏,废话少说,Leetcode 刷起来呀。说起刷 Leetcode,我建议你按 tag 刷,不然只能像无头苍蝇,东一榔头西一棒槌,最后事倍功半 (过来人的惨痛经历)。最近正好在刷 Leetcode 上的链表题,也碰到了一些颇具代表性的题型,正好做个记录,也分享给有需要的小伙伴。 对链表不太熟悉的小伙伴碰...

IamHYN的博客 624

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

单链表反转,我们从递归说起,图解带你彻底搞懂

单链表反转,我们从递归说起,图解带你彻底搞懂....递归和迭代,看完图解和动画,豁然开朗!

iqqcode 4004

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

【LeetCode训练营】反转链表 移除链表元素 详细图解 203,206

请你反转链表,并返回反转后的链表。,请你删除链表中所有满足。列表中的节点数目在范围。给你一个链表的头节点。

陈大大陈的博客 2248

看一遍就理解,图解单链表反转

前言反转链表是程序员必备的基本素养,经常在面试、笔试的过程中出现。一直觉得反转链表实现代码不是很好理解,决定搬leetcode那道经典反转链表题出来,用十多张图去解析它,希望加深大家对链...

weiwenhou的博客 1640

图解链表反转的过程 -- leetCode 题号:206

这里写自定义目录标题图解链表反转的过程 -- leetCode 题号:206概述题目描述官方答案图解过程第一次迭代:第二次迭代总结 图解链表反转的过程 – leetCode 题号:206 概述 本人算法小白,在遇到单链表反转的这道题目的时候,很是不理解为什么这么写,经过画图一步一步的走,可算是理解这个整体的过程了。 题目描述 题目描述:反转一个单链表。 示例: 输入: 1->2-&gt...

微信公众号《后端学长》 313

Leetcode 102 反转链表

反转链表的核心是「先保存下一个节点,再反转当前指针,最后移动双指针」,顺序绝不能乱;循环条件只需要cur!= nullptr,不用加cur->next!= nullptr,否则最后一个节点处理不到;期末阶段不用追求「算法感悟」,先把这道题的代码背熟、步骤记牢,能稳稳写出正确代码拿分就够了。你已经在坚持打卡了,这就比很多人强!最难的日子一定会过去,先把这道基础题吃透,一步一个脚印,过线肯定没问题~链表的指针操作里,「修改地址」和「访问地址」是互斥的—— 你一旦修改了cur->next。

im_AMBER的博客 1058

【LeetCode题目详解】(二)206.反转链表、876.链表的中间结点

【LeetCode题目详解】(二)206.反转链表、876.链表的中间结点

青色_忘川的博客 1168

力扣 反转链表(图解)

力扣 反转链表(图解)

m0_62750441的博客 581

LeetCode反转链表图解

LeetCode206:反转链表(Easy) 题目 图解 代码详解: class Solution{ private ListNode reverseNodeList(ListNode head){ //1.定义三个节点,分别是pre,cur,nex ListNode pre = null; ListNode cur = head; ListNode nex = null; //2.链表进行反转,当cur为null停止循环 while(cur != null){

保持学习 1154

【力扣-206】反转链表

🖊作者 : Djx_hmbb📘专栏 : 数据结构😆今日分享 :----------“覆蕉寻鹿”:有的人把假的当成真的,而又有人把明明是真的事情当成假的,前者可以说是“黄粱美梦”,后面这种就是“覆蕉寻鹿”。以前有个樵夫打死了一头野鹿,因为一时带不回家,怕被其他人发现,就先用芭蕉叶把牠覆盖起来;后来樵夫去找鹿,忘记了所藏的地方,怎么也找不到,最后以为是自己做了一场梦。所以用“覆蕉寻鹿”来比喻把真实的事情看作梦幻的情况,也可用“覆鹿寻蕉”,都是一个意思。------谢一。

Djx_hmbb的博客 569
上一篇: 无人机使能的边缘计算优化问题
下一篇: 八股打卡(一)
code120302
博客等级 码龄7年 195粉丝 38原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值