一. 序

链表作为一种基本的数据结构,本身理解起来,很简单。它通过指针或者叫引用,将一组零散的内存空间(结点),串联起来组成一个数据存储结构。

链表根据其指针的指向和丰富程度,可以分为单链表、双向链表、循环链表、双向循环链表。其差别就是,是否在单链表的基础上为结点,增加更丰富的指针,让其实现更丰富的功能。

链表虽然很好理解,但是链表的代码,写起来却并不是那么容易,尤其上一些对单链表的操作,例如链表反转、链表双双反转、有序链表合并等。

你可以自己试试,放下手机拿起纸笔,来一场模拟面试,就是写一个单链表两两反转,看看能否一次通过。

写链表代码的时候,指针指来指去,很容易就把指针丢失,造成链表断裂。所以在操作链表时,其操作顺序就是我们着重关注的点。

虽然链表代码写起来不容易,但链表又是面试的常客,一些常见的算法实现,也是我们开发者必须要掌握的。

之前写过单链表反转的三种解法(戳我了解),今天再来聊聊它的升级版,链表两两反转,这也是 Leetcode 第 24 题,算是面试常客了。

二. 单链表两两反转

2.1 什么是单链表两两反转?

单链表反转比较好理解,就是逆序嘛,但是两两反转是什么意思呢?

我们知道,单链表是由指针,将一个一个结点串联起来的数据结构。那么我们将这些结点,两个为一组,在组内进行反转,就是两两反转了。

单链表两两反转这种题,非常适合用递归的思想来解决,将每一步操作都封闭在一个小单元内,然后重复操作。

通常递归能做的,循环也能做,所以我们就这两种解法,分别讲解。

2.2 循环解法

无论是使用循环还是递归,其实都是将链表结点交换的步骤拆解,放在一个个小循环(递归)中去处理。相对于递归,循环法在结点的使用步骤上更清晰,我们就以循环法作为切入点。

递归或循环,其核心就是找到抽象模型,在每个调用步骤中,不断重复相同的事情。

在单链表两两反转中,看似是在处理两个结点,但其实是在处理 4 个结点之间的关系。

除了待反转的 A、B 两个结点之外,还需要操作 A 的前驱结点 prev 结点和 B 的 Next 结点 b-next 结点。

我们每次反转,其实就在操作这四个结点,其中的操作步骤很重要。

如图所示,步骤 ① 操作有两步操作,因为其操作的指针互不影响,所以在写代码的时候不分先后,在保证 prev 指针和 b-next 指针的指向无错后,就可以开始 A、B 结点的反转,也就是步骤 ②。

最后我们只需要将我们关注的结点前移,就可以进入下一次循环。

在这个步骤中,我们在操作 4 个结点的指针,但是其实每次初始的结点,只有 A 的前驱结点 prev 结点。为了保证循环内的操作一致,我们可以在链表前,加一个虚拟的头结点,来辅助我们,让代码更简洁。

到这里,各个步骤就清晰了,每次反转两个结点,然后前移 dummy 指针。

最后,还需要再注意一些边界条件,注意我们的循环,什么时候停止。

在单链表两两反转的场景下,链表的结点数,有单有双,当结点数为单数时,最后一个结点已经找不到可以反转交换的结点了,此时保持不变即可。

接下来直接上循环代码了,这里使用我们熟悉的 Java 代码来实现。

public ListNode swapPairs(ListNode head){ // 链表头增加虚拟结点 dummy ListNode dummy = new ListNode(-1); dummy.next = head; head = dummy; // 循环退出条件,注意链表结点数单双的情况 while(head.next != null && head.next.next != null){ // 开始反转 ListNode a = head.next; ListNode b = a.next; head.next = b; // 步骤① a.next = b.next; // 步骤① b.next = a; // 步骤② // dummy 指针前移 head = a; } return dummy.next;}

代码中的注释已经很清晰了,首先在链表头插入一个虚拟结点 dummy,之后开启循环,循环退出的条件就是走到了链表尾部的边界,需要注意结点数为单、双两种情况。之后再按照前文中图解的步骤,开始操作链表指针实现两两反转,最后前移 dummy 指针。

2.3 递归解法

递归的解法,相对于循环解法,代码量上就少很多,看着也清爽了。主要是因为递归,通过一层层的调用,在方法栈上存储了存储了一些变量就是我们待操作的结点。

在这里,在递归里,我们依然关注三个问题,递归解决的小问题、终止条件以及返回值。

递归的解法,直接看代码比较清晰。

public ListNode swapPairs(ListNode head) { if (head == null || head.next == null) { return head; } ListNode next = head.next; head.next = swapPairs(next.next); next.next = head; return next;}

再结合之前操作步骤的图解。

递归的方法比较绕,结合上图,找到思路,循环是一次从前向后的移动操作窗口,而递推是从后向前移动操作窗口。注意递归终止条件以及每次递归操作时,结点指针的轮转,多想多练就清晰了。

三. 小结时刻

到这里对单链表的两两反转,就讲解完毕。

写链表代码,除了考验逻辑思维能力,还考验编码能力,多写多练才是核心。

注意其中的边界条件以及每个操作单元中,结点指针的交换轮转。这其中的每个步骤的操作顺序,都通过图解的方式讲解清楚了,有疑问欢迎在留言去讨论。

本文对你有帮助吗?留言、转发、点赞是最大的支持,谢谢!


在头条号私信我。我会送你一些我整理的学习资料,包含:Android反编译、算法、设计模式、虚拟机、Linux、Kotlin、Python、爬虫、Web项目源码。

单链表的合并算法_图解算法:单链表两两反转 | 眼睛会了手就会系列相关推荐

  1. cb32a_c++_STL_算法_查找算法_(5)adjacent_find

    cb32a_c++_STL_算法_查找算法_(5)adjacent_find adjacent_find(b,e),b,begin(),e,end() adjacent_find(b,e,p),p-p ...

  2. 常用十大算法_回溯算法

    回溯算法 回溯算法已经在前面详细的分析过了,详见猛击此处. 简单的讲: 回溯算法是一种局部暴力的枚举算法 循环中,若条件满足,进入递归,开启下一次流程,若条件不满足,就不进行递归,转而进行上一次流程. ...

  3. 上标3下标6算法_图解堆排序算法

    堆排序定义 一般来说,算法就像数学公式,前人经过不断优化和验证得到有规律性的公式留给后人使用,当然也会交给后人验证的思路.那么堆排序算法就是这样,它有基本固定的定义如下: 1.将数组构建为一颗有规则的 ...

  4. 滴滴派单算法_从算法模型思路到评估方案 - 详解

    导读:说到滴滴的派单算法,大家可能感觉到既神秘又好奇,从出租车扬召到司机在滴滴平台抢单最后到平台派单,大家今天的出行体验已经发生了翻天覆地的变化,面对着每天数千万的呼叫,滴滴的派单算法一直在持续努力让 ...

  5. java算法判断链表有没有闭环_前端算法系列之二:数据结构链表、双向链表、闭环链表、有序链表...

    前言 上一次我们讲到了数据结构:栈和队列,并对他们的运用做了一些介绍和案例实践:我们也讲到了怎么简单的实现一个四则运算.怎么去判断标签是否闭合完全等等,anyway,今天接着和大家介绍一些数据结构: ...

  6. 链表节点合并排序:数组和单链表

    每日一贴,今天的内容关键字为链表节点 1 数组合并排序 1.1 合并两个已排序好的数组 需要额定的存储空间用来存储合并结果 //merge two array which are already so ...

  7. python序列模式的关联算法_关联算法

    以下内容来自刘建平Pinard-博客园的学习笔记,总结如下: 1 Apriori算法原理总结 Apriori算法是常用的用于挖掘出数据关联规则的算法,它用来找出数据值中频繁出现的数据集合,找出这些集合 ...

  8. 蛮力写算法_蛮力算法解释

    蛮力写算法 Brute Force Algorithms are exactly what they sound like – straightforward methods of solving a ...

  9. 无头结点单链表的逆置_从无头单链表中删除节点及单链表的逆置

    题目: 假设有一个没有头指针的单链表.一个指针指向此单链表中间的一个节点(非第一个节点, 也非最后一个节点).请将该节点从单链表中删除. 解答: 典型的"狸猫换太子", 若要删除该 ...

最新文章

  1. RabbitMQ和Kafka到底怎么选?
  2. 一个小改动,CNN输入固定尺寸图像改为任意尺寸图像
  3. tomcat安装配置及应用
  4. Delphi中静态方法重载还是覆盖的讨论
  5. linux 修改超级权限密码,linux 修改用户密码
  6. fopen需要改写成fopen_s的时候
  7. VTK:隐式布尔值demo用法实战
  8. C4.5决策树生成算法完整版(Python),连续属性的离散化, 缺失样本的添加权重处理, 算法缺陷的修正, 代码等
  9. java 一些容易忽视的小点-数据类型和运算符篇
  10. HDU2548 两军交锋【数学计算+水题】
  11. Java基础篇:如何使用圆括号
  12. Samsung GT-P5100刷andorid 4.3
  13. 【OCP题库-12c】最新CUUG OCP 071考试题库(72题)
  14. 麟龙指标通达信指标公式源码_麟龙指标套三 麟龙特色指标 通达信主图+副图指标 贴图...
  15. 电容或电感的电压_纯电阻、纯电感和纯电容电路
  16. 怎么让热图显示基因名_如何将 qPCR 数据做成热图
  17. PPT设置自动保存时间 mac_PPT又崩溃了?教你如何恢复文档
  18. 2023-01-03 有关C++继承多态的一点想法
  19. 【python数据分析】足球运动员的特征分析
  20. 国密sm4 ECB、CEC模式探究与在iOS中的应用

热门文章

  1. 基于JAVA+SpringMVC+Mybatis+MYSQL的精美酒店管理系统
  2. (转)认识原型对象和原型链
  3. python实现以及所有排序大总结【转】
  4. java中常忽视的小问题
  5. Webservice学习之——即时发布与定制发布
  6. C++ string和c类型字符数组的比较
  7. POJ 3630 Phone List [Trie]
  8. c# default 关键字
  9. DriverMessageBean配置详解
  10. 终于找到了满意的博客主题