[LeetCode] 24. 两两交换链表中的节点
题目描述
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 :
给定 1->2->3->4, 你应该返回 2->1->4->3.
分析
一、递归
我们假设链表只有三个结点,交换前两个结点。交换后,头结点应连向原来的第三个结点,第二个结点连向第一个结点,交换完成,第二个结点就是新的头结点了。但我们要先记下第二个结点,不然头结点连向了第三个结点,第二个结点就丢失了。
public ListNode swapPairs(ListNode head) { ListNode newHead = head.next; head.next = head.next.next; newHead.next = head; return newHead; }
这样只是处理了一次,要递归处理,我们就先进入递归去处理最后面的结点,然后再一路返回。就是先把每次的第三个结点当作头结点进行处理。我们把
head.next = head.next.next
改成head.next = swapPairs(head.next.next)
就可以了。这样我们的操作其实就已经完成了,接下来就是判断递归终止的条件,两个结点以上才能交换,所以终止条件应是头结点为空,或头结点的下一结点为空。
if (head == null || head.next == null) { return head; }
二、迭代
我们先定义一个哑结点 dumb,把它与头结点相连,并定义一个 pre 结点,pre 结点始终表示当前进行交换的第一个结点的上一个结点,起始时即 dumb。
ListNode dumb = new ListNode(0), pre = dumb; dumb.next = head;
每次遍历,我们用 first 表示这次循环要交换的两个结点的第一个,second 表示第二个。像递归那样,我们还先假设链表只有三个结点,我们先把 first 连向第三个结点,second 连向 first,此时已交换了两个结点。
ListNode first = pre.next; ListNode second = pre.next.next; first.next = second.next; second.next = first;
但此时前一个结点仍然连向 first,应连回 second,交换后的 second 才是前一个结点的下一个结点。pre 结点始终表示当前进行交换的第一个结点的上一个结点,此轮的两个结点已交换,准备下一轮,pre 应更新为第三个结点的上一个结点,即 first ,因为 first 和 second 已经完成了交换,此时 first 结点就是第二个结点。
pre.next = second; pre = first;
循环进行的条件应该是当前进行交换的两个结点均不为空,即
pre.next != null && pre.next.next != null
。最后我们应该返回的是 dumb 的下一个结点 dumb.next。
代码
解法一:递归
class Solution {
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode newHead = head.next;
head.next = swapPairs(head.next.next);
newHead.next = head;
return newHead;
}
}
解法二:迭代
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dumb = new ListNode(0), pre = dumb;
dumb.next = head;
while (pre.next != null && pre.next.next != null) {
ListNode first = pre.next;
ListNode second = pre.next.next;
first.next = second.next;
second.next = first;
pre.next = second;
pre = first;
}
return dumb.next;
}
}
小结
个人觉得递归法很好理解,反而是迭代法有点绕,不过画一下图也就清楚了。
我发现,递归结束的条件和迭代进行的条件又是刚好相反。
原文地址:https://www.cnblogs.com/qiu_jiaqi/p/LeetCode_24.html
- 【翻译】ES6生成器简介
- 浅谈事件冒泡
- Github page搭建博客使用自定义插件的方法
- 【翻译】JavaScript中5个值得被广泛使用的数组方法
- 【翻译】浏览器渲染Rendering那些事:repaint、reflow/relayout、restyle
- Entity Framework Core 实现MySQL 的TimeStamp/RowVersion 并发控制
- 《JQuery技术内幕》读书笔记——自调用匿名函数剖析
- 【代码+论文】通过ML、Time Series模型学习股价行为
- .NET Core 系列5 :使用 Nuget打包类库
- 解决transition动画与display冲突的几种方法
- Gof设计模式之装饰者模式(七)
- JavaScript递归中的作用域问题
- constructor属性解析
- Paxos协议学习小结
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- edu资产收集(主域名)
- 如何创建一个可复用的网页爬虫
- 《剑指offer》第十天:青蛙跳台阶
- Kettle构建Hadoop ETL实践(一):ETL与Kettle
- 初识ABP vNext(2):ABP启动模板
- 维度爆炸?Python实现数据压缩竟如此简单!
- 如何扩展单个Prometheus实现近万Kubernetes集群监控?
- 一文带你彻底厘清 Isito 中的证书工作机制
- 如何将第三方服务注册集成到 Istio ?
- 【Pod Terminating原因追踪系列】之 containerd 中被漏掉的 runc 错误信息
- 【Pod Terminating原因追踪系列之二】exec连接未关闭导致的事件阻塞
- CD+服务网格灰度发布实践,一文带你体验如何编排更灵活
- 花十分钟的时间武装你的代码库
- 对HTML-input的一些思考和理解
- 【投稿】刀哥:Rust学习笔记 1