算法:反转单链表

本贴最后更新于 1331 天前,其中的信息可能已经事过景迁

对 leetCode 一个算法的分析学习,支持对单链表内指定区间的反转实现。
206. 反转链表
92. 反转链表 II

import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; /** * 对leetCode一个算法的分析学习 * 题目:单链表的反转 * * @author hudk * @date 2020/5/27 20:20 */ public class Solution { /** * 单链表结点 */ public static class ListNode { int val; public ListNode next; public ListNode(int x) { val = x; } @Override public String toString() { return String.valueOf(val); } } /** * leetCode 题目:反转单链表1 * * 示例: * 输入: 1->2->3->4->5->NULL * 输出: 5->4->3->2->1->NULL * 进阶: * 你可以迭代或递归地反转链表。你能否用两种方法解决这道题? * * 来源:力扣(LeetCode) * 链接:https://leetcode-cn.com/problems/reverse-linked-list * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 * @param head * @return */ /** * 方法一 * 这个方法是本人解答方案,相对使用了比较多的空间 * 空间复杂度:O(n) * 时间复杂度:O(n) * * @param head * @return */ public static ListNode myReverseList(ListNode head) { //当链表长度为零时,直接返回null if (head == null) { return null; } //引用指向头结点 ListNode h = head; //遍历整个链表,统计链表长度 i int i = 1; while (h.next != null) { i++; h = h.next; } //创建一个和链表长度一样的数组,并将链表的元素按照原顺序逐个放入数组中 ListNode[] ln = new ListNode[i]; for (int j = 0; j < i; j++) { ln[j] = head; head = head.next; } //再从数组的尾部开始遍历,逐个该表链表元素的next指针指向前一个元素。 for (int x = i - 1; x > 0; x--) { ln[x].next = ln[x - 1]; } //将原来的头结点(现在转置后的尾结点next引用置空) ln[0].next = null; //返回转置后新的头结点 return ln[i - 1]; } /** * 方法二 * 这个方法是leetCode上的算法大神解答的方案 * 利用递归的巧妙与优雅实现 * * 作者:labuladong * 链接:https://leetcode-cn.com/problems/reverse-linked-list-ii/solution/bu-bu-chai-jie-ru-he-di-gui-di-fan-zhuan-lian-biao/ * 来源:力扣(LeetCode) * 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 * * @param head * @return */ public static ListNode revers(ListNode head) { if (head.next == null) { return head; } ListNode last = revers(head.next); head.next.next = head; head.next = null; return last; } /** * 方法三 * 这个方法是官方解答的方案 * 利用了迭代的思想,同样简洁且高效 * 空间复杂度:O(1) * 时间复杂度:O(n) * * 来源:力扣(LeetCode) * 链接:https://leetcode-cn.com/problems/reverse-linked-list * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 * * @param head * @return */ public static ListNode reverseList(ListNode head) { ListNode prev = null; ListNode curr = head; ListNode nextTemp = null; while (curr != null) { nextTemp = curr.next; curr.next = prev; prev = curr; curr = nextTemp; } return prev; } /** * leetCode 题目:反转单链表2 * 反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。 * * 说明: * 1 ≤ m ≤ n ≤ 链表长度。 * * 示例: * 输入: 1->2->3->4->5->NULL, m = 2, n = 4 * 输出: 1->4->3->2->5->NULL * * 来源:力扣(LeetCode) * 链接:https://leetcode-cn.com/problems/reverse-linked-list-ii * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 */ /** * 方法一 begin******************************************************************* * <p> * 递归实现单链表的指定区间反转 * 这个算法的实现,淋漓尽致的体现了递归的优雅与简洁。 * 适用于链表长度比较短的场景,或对性能要求不高的场景 * <p> * 作者:labuladong * 链接:https://leetcode-cn.com/problems/reverse-linked-list-ii/solution/bu-bu-chai-jie-ru-he-di-gui-di-fan-zhuan-lian-biao/ * 来源:力扣(LeetCode) * 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 * * @param head * @param m * @param n * @return */ public static ListNode reverseBetween(ListNode head, int m, int n) { if(head == null){ return null; } // base case if (m == 1) { //单链表的前n个结点反转 return reverseN(head, n); } // 前进到反转的起点触发 base case head.next = reverseBetween(head.next, m - 1, n - 1); return head; } /** * 递归实现单链表的前n个结点反转 */ static ListNode successor = null; // 后驱节点 // 反转以 head 为起点的 n 个节点,返回新的头结点 public static ListNode reverseN(ListNode head, int n) { if(head == null){ return null; } if (n == 1) { // 记录第 n + 1 个节点 successor = head.next; return head; } // 以 head.next 为起点,需要反转前 n - 1 个节点 ListNode last = reverseN(head.next, n - 1); head.next.next = head; // 让反转之后的 head 节点和后面的节点连起来 head.next = successor; return last; } /**方法一end*******************************************************************/ /** * 方法二 * 这是本人的解答方案,使用了迭代的思路,并将各种情况逐一考虑,分别处理。 * 代码量比较多,但自我感觉逻辑看起来更加清晰一些。 * * @param head 链表头结点 * @param m 翻转区间开始位置 * @param n 翻转区间结束位置 * @return */ public static ListNode myReverseBetween(ListNode head, int m, int n) { if(m <= 0){ m = 1; } int size = size(head); if(n > size){ n = size; } //如果传入为空,直接返回空 if (head == null) { return null; } //如果m = n,说明转置后等于没转置,所以不做处理直接返回原链表 if (m == n) { return head; } //1、当m等于1时,倒序之后,第一个结点一定会和第n+1个结点相连 //2、并且第n个结点,会成为新链表的头结点 if (m == 1) { ListNode perv = null; ListNode crr = head; ListNode nodeOne = null; int i = 1; while (crr != null) { ListNode next = crr.next; if (i == 1) { //暂时记住第一个结点,后面它将会与第n+1个结点相连 nodeOne = crr; nodeOne.next = null; //为迭代做准备,从第2个结点开始迭代地做“翻转”动作,故将第1个结点当做下次循环时的“前置结点” perv = crr; } //从第二个结点开始,一直到第n个结点,逐一"翻转"他们的next if (i > m && i < n) { crr.next = perv; perv = crr; } //第n个结点 if (i == n) { //第一个结点的的next引用指向了第n+1个结点 nodeOne.next = crr.next; //翻转第n个结点的next crr.next = perv; //第n个结点,会成为新链表的头结点 head = crr; //由于后面得结点不需要做处理了,故跳出循环即可 break; } //迭代 crr = next; i++; } } //1、如果m>1,第m-1个结点会与第n个结点相连,第m个结点会与第n+1个结点相连 //2、然后,第m+1个到第n个结点的next依次“翻转” //3、头结点不变 if (m > 1) { ListNode perv = null; ListNode crr = head; ListNode nodeMp = null;//第m个结点的前一个结点 ListNode nodeM = null;//第m个结点 int i = 1; while (crr != null) { ListNode next = crr.next; if (i == m - 1) { //暂时记住第m-1个结点,后面它将会与第n个结点相连 nodeMp = crr; nodeMp.next = null; } if (i == m) { //暂时记住第m个结点,后面它将会与第n+1个结点相连 nodeM = crr; nodeM.next = null; //为迭代做准备,从m+1个结点开始迭代地做“翻转”动作,故将第m个结点当做下次循环时的“前置结点” perv = crr; } //第m+1个到第n个结点的next依次“翻转” if (i > m && i < n) { crr.next = perv; perv = crr; } if (i == n) { //第m个结点的next引用指向了第n+1个结点 nodeM.next = crr.next; //翻转第n个结点的next crr.next = perv; //第m-1个结点的next引用指向了第n个结点 nodeMp.next = crr; //由于后面得结点不需要做处理了,故跳出循环即可 break; } //迭代 crr = next; i++; } } return head; } /**方法二end*******************************************************************/ /** * 测试用例 * @param args */ public static void main(String[] args) { //生成一个长度为10的单链表 ListNode head = createRandomSingleLinkList(10); //打印初始链表 printLinkList(head); //反转测试 ListNode head1 = myReverseList(head); printLinkList(head1); //迭代方式反转测试 ListNode head2 = reverseList(head1); printLinkList(head2); //递归方式反转测试 ListNode head3 = revers(head2); printLinkList(head3); //迭代方式反转指定区间测试 ListNode head4 = myReverseBetween(head3,2,8); printLinkList(head4); //递归方式反转指定区间测试 ListNode head5 = reverseBetween(head4,4,5); printLinkList(head5); } /** * 生成一个指定长度的链表 * @param size * @return */ public static ListNode createRandomSingleLinkList(int size){ if(size == 0){ return null; } ListNode head = new ListNode(1); ListNode crr= head; for(int i=1; i<size; i++){ ListNode node = new ListNode(i+1); crr.next = node; crr = node; } return head; } /** * 生成 0 - 100 范围内的随机正式 * @return */ public static int randomInt(){ return (int)Math.floor(Math.random()*100); } /** * 打印链表 * @param head */ public static void printLinkList(ListNode head){ List<ListNode> nodes = new ArrayList<>(); if(head == null){ System.out.println(nodes); return; } nodes.add(head); while (head.next != null){ nodes.add(head.next); head = head.next; } System.out.println(nodes); } /** * 计算单链表长度 * @param head * @return */ public static int size(ListNode head){ if(head == null){ return 0; } int i = 1; while (head.next != null){ head = head.next; i++; } return i; } }
1 操作
hudk 在 2021-10-11 10:53:17 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • OnlyOffice
    4 引用 • 26 关注
  • WiFiDog

    WiFiDog 是一套开源的无线热点认证管理工具,主要功能包括:位置相关的内容递送;用户认证和授权;集中式网络监控。

    1 引用 • 7 回帖 • 611 关注
  • GitHub

    GitHub 于 2008 年上线,目前,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。正因为这些功能所提供的便利,又经过长期的积累,GitHub 的用户活跃度很高,在开源世界里享有深远的声望,并形成了社交化编程文化(Social Coding)。

    209 引用 • 2040 回帖
  • WebComponents

    Web Components 是 W3C 定义的标准,它给了前端开发者扩展浏览器标签的能力,可以方便地定制可复用组件,更好的进行模块化开发,解放了前端开发者的生产力。

    1 引用 • 8 关注
  • Pipe

    Pipe 是一款小而美的开源博客平台。Pipe 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    134 引用 • 1127 回帖 • 110 关注
  • 安装

    你若安好,便是晴天。

    132 引用 • 1184 回帖
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    524 引用 • 4601 回帖 • 709 关注
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    201 引用 • 120 回帖
  • 机器学习

    机器学习(Machine Learning)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。

    77 引用 • 37 回帖
  • Electron

    Electron 基于 Chromium 和 Node.js,让你可以使用 HTML、CSS 和 JavaScript 构建应用。它是一个由 GitHub 及众多贡献者组成的活跃社区共同维护的开源项目,兼容 Mac、Windows 和 Linux,它构建的应用可在这三个操作系统上面运行。

    15 引用 • 136 回帖 • 7 关注
  • C

    C 语言是一门通用计算机编程语言,应用广泛。C 语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

    86 引用 • 165 回帖 • 1 关注
  • Scala

    Scala 是一门多范式的编程语言,集成面向对象编程和函数式编程的各种特性。

    13 引用 • 11 回帖 • 157 关注
  • 招聘

    哪里都缺人,哪里都不缺人。

    188 引用 • 1057 回帖
  • Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的操作系统上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在机器和数据中心中运行。

    496 引用 • 934 回帖
  • Chrome

    Chrome 又称 Google 浏览器,是一个由谷歌公司开发的网页浏览器。该浏览器是基于其他开源软件所编写,包括 WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。

    63 引用 • 289 回帖
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 403 关注
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    20 引用 • 245 回帖 • 227 关注
  • OpenResty

    OpenResty 是一个基于 NGINX 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

    17 引用 • 52 关注
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    730 引用 • 1281 回帖 • 1 关注
  • 小薇

    小薇是一个用 Java 写的 QQ 聊天机器人 Web 服务,可以用于社群互动。

    由于 Smart QQ 从 2019 年 1 月 1 日起停止服务,所以该项目也已经停止维护了!

    35 引用 • 468 回帖 • 762 关注
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 650 关注
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 704 关注
  • SendCloud

    SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。

    2 引用 • 8 回帖 • 502 关注
  • IBM

    IBM(国际商业机器公司)或万国商业机器公司,简称 IBM(International Business Machines Corporation),总公司在纽约州阿蒙克市。1911 年托马斯·沃森创立于美国,是全球最大的信息技术和业务解决方案公司,拥有全球雇员 30 多万人,业务遍及 160 多个国家和地区。

    17 引用 • 53 回帖 • 147 关注
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 78 关注
  • 运维

    互联网运维工作,以服务为中心,以稳定、安全、高效为三个基本点,确保公司的互联网业务能够 7×24 小时为用户提供高质量的服务。

    151 引用 • 257 回帖
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 384 回帖 • 7 关注