递归反转链表的一部分
递归反转链表的一部分
反转单链表的迭代实现
不是⼀个困难的事情, 但是递归实现
就有点难度了,如果再加⼀点难度, 让你仅仅反转单链表中的⼀部分, 你是否能够递归实现呢?
本⽂就来由浅⼊深, 逐步地解决这个问题。 如果你还不会递归地反转单链表也没关系, 本⽂会从递归反转整个单链表开始拓展, 只要你明⽩单链表的结构, 相信你能够有所收获
// 单链表节点的结构
public class ListNode {int val;ListNode next;ListNode(int x) { val = x; }
}
什么叫反转单链表的⼀部分呢, 就是给你⼀个索引区间, 让你把单链表中这部分元素反转, 其他部分不变:
迭代的思路⼤概是: 先⽤⼀个 for 循环找到第 m 个位置, 然后再⽤⼀个 for 循环将 m 和 n 之间的元素反转。
但是我们的递归解法不⽤⼀个 for 循环, 纯递归实现反转
⼀、 递归反转整个链表
ListNode reverse(ListNode head) {if (head.next == null) return head;ListNode last = reverse(head.next);head.next.next = head;//相当于把head的next个节点的next指针指向自己//此时head节点和head的next节点之间相当于双向链表//断开head指向next个节点的指针,因为代码走到这里说明//head的下一个next节点的next指针已经指向head节点了//完成反转功能head.next = null;return last;
}
我们下⾯来详细解释⼀下这段代码
对于递归算法, 最重要的就是明确递归函数的定义
。 具体来说, 我们的reverse 函数定义是这样的:输⼊⼀个节点 head , 将「以 head 为起点」 的链表反转, 并返回反转之后的头结点。
明⽩了函数的定义, 在来看这个问题。 ⽐如说我们想反转这个链表:
那么输⼊ reverse(head) 后, 会在这⾥进⾏递归:
ListNode last = reverse(head.next);
不要跳进递归(你的脑袋能压⼏个栈呀? )
, ⽽是要根据刚才的函数定义,来弄清楚这段代码会产⽣什么结果:
这个 reverse(head.next)
执⾏完成后, 整个链表就成了这样:
并且根据函数定义, reverse 函数会返回反转之后的头结点
, 我们⽤变量last 接收了
现在再来看下⾯的代码:
head.next.next = head;
接下来:
head.next = null;
return last;
神不神奇, 这样整个链表就反转过来了! 递归代码就是这么简洁优雅, 不过其中有两个地⽅需要注意:
1、 递归函数要有 base case, 也就是这句:
if (head.next == null) return head;
意思是如果链表只有⼀个节点的时候反转也是它⾃⼰, 直接返回即可。
2、 当链表递归反转之后, 新的头结点是 last , ⽽之前的 head 变成了最后⼀个节点, 别忘了链表的末尾要指向 null:
head.next = null;
理解了这两点后, 我们就可以进⼀步深⼊了, 接下来的问题其实都是在这个
算法上的扩展。
⼆、 反转链表前 N 个节点
这次我们实现⼀个这样的函数:
// 将链表的前 n 个节点反转(n <= 链表⻓度)
ListNode reverseN(ListNode head, int n)
⽐如说对于下图链表, 执⾏ reverseN(head, 3)
:
解决思路和反转整个链表差不多, 只要稍加修改即可:
ListNode successor = null; // 后驱节点// 反转以 head 为起点的 n 个节点,返回新的头结点
ListNode reverseN(ListNode head, int n) {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;
}
具体的区别:
1、 base case 变为 n == 1 , 反转⼀个元素, 就是它本⾝, 同时要记录后驱
节点。
2、 刚才我们直接把 head.next 设置为 null, 因为整个链表反转后原来的head 变成了整个链表的最后⼀个节点。 但现在 head 节点在递归反转之后不⼀定是最后⼀个节点了, 所以要记录后驱 successor (第 n + 1 个节点) , 反转之后将 head 连接上。
三、 反转链表的⼀部分
现在解决我们最开始提出的问题, 给⼀个索引区间 [m,n] (索引从 1 开始) , 仅仅反转区间中的链表元素
。
ListNode reverseBetween(ListNode head, int m, int n)
⾸先, 如果 m == 1 , 就相当于反转链表开头的 n 个元素
嘛, 也就是我们刚才实现的功能:
ListNode reverseBetween(ListNode head, int m, int n) {// base caseif (m == 1) {// 相当于反转前 n 个元素return reverseN(head, n);}// ...
}
如果 m != 1
怎么办?如果我们把 head 的索引视为 1
,那么我们是想从第 m 个元素开始反转对
吧;如果把 head.next 的索引视为 1
呢?那么相对于 head.next,反转的区间应该是从第 m - 1 个元素开始
的;那么对于 head.next.next 呢……
区别于迭代思想,这就是递归思想,所以我们可以完成代码:
ListNode reverseBetween(ListNode head, int m, int n) {// base caseif (m == 1) {return reverseN(head, n);}// 前进到反转的起点触发 base casehead.next = reverseBetween(head.next, m - 1, n - 1);return head;
}
四、 最后总结
递归的思想相对迭代思想, 稍微有点难以理解, 处理的技巧是: 不要跳进递归, ⽽是利⽤明确的定义来实现算法逻辑。
递归操作链表并不⾼效
。 和迭代解法相⽐, 虽然时间复杂度都是 O(N)
, 但是迭代解法的空间复杂度是 O(1)
, ⽽递归解法需要堆栈, 空间复杂度是 O(N)
。
递归反转链表的一部分相关推荐
- 递归删除单链表中所有值为x的元素_如何纯递归反转链表的一部分
读完本文,你可以去力扣拿下如下题目: 92.反转链表II ----------- 反转单链表的迭代实现不是一个困难的事情,但是递归实现就有点难度了,如果再加一点难度,让你仅仅反转单链表中的一部分,你是 ...
- 递归反转链表改变原链表吗_在不使用递归的情况下找到链表的长度
递归反转链表改变原链表吗 Solution: 解: Algorithm to find length 查找长度的算法 Input: 输入: A singly linked list whose add ...
- 递归 反转链表 c语言程序,C语言反转链表的递归算法
#includetypedef struct link { int data; struct link *next; }node; node *createList() { node *pre, *c ...
- 反转链表JAVA算法_链表反转算法
1 packagecom.trs.codetool.sort;2 3 /** 4 *@authorzheng.changgang5 * @date 2020-01-02 09:576 * 链表的常见算 ...
- C++递归与非递归实现链表的反转
思想参考:http://ceeji.net/blog/reserve-linked-list-cpp/ #include"list.h" using namespace std; ...
- [Leedcode][JAVA][第25题][K个一组反转链表][链表][递归]
[问题描述][第25题][K个一组反转链表][困难] 时间复杂度:O(N^2) 空间复杂度:O(1) ```java 给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表.k 是一个正整数, ...
- 剑指Offer - 面试题24. 反转链表(遍历/递归)
1. 题目 定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点. 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4-&g ...
- 剑指Offer - 面试题6. 从尾到头打印链表(栈,递归,反转链表)
文章目录 1. 题目 2. 解题 2.1 stack解题 2.2 递归 2.3 反转链表 1. 题目 输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回). 示例 1: 输入:head ...
- 反转链表 python 递归_LeetCode 206.反转链表(Python3)
题目: 反转一个单链表. 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 进阶: 你 ...
最新文章
- php stream 系列函数,PHP stream 系列函数使用遇到的问题??
- 自然语言处理中的语言模型与预训练技术的总结
- 自定义控件被忽略的渲染性能
- mysql sumif优化,sumif函数的使用方法
- 数学建模常用解题方法
- DG半离散格式的转化---基于matlab编写
- keil安装GD32 pack包安装不上 不显示 没有了
- Unity 查找资源引用
- Python爬网易云音乐的那些事
- 2017年总结和2018年展望
- 新服务器如何设置共享硬盘,Windows Server 2012 iSCSI如何搭建共享磁盘教程
- Tableau画地图
- python抓取朋友圈动态_如何利用Python网络爬虫爬取微信朋友圈动态--附代码(下)...
- Clojure学习03:数据结构(集合)
- 蜂鸣器、风扇、震动马达
- 华为FreeBuds SE耳机有杂音异响的解决办法
- IE if注释判断( [if gte IE 8] )的解释网上的完全乱七八糟啊!
- 【英语语法入门】第40讲 原形不定式(1)使役动词
- 2018年9月12日
- 前端 实现数据的分批加载
热门文章
- 图文直播:Pokémon Go真爱粉与“脑残粉”之间的爱恨情仇
- MySQL授权用户及密码恢复设置
- SpringMVC @RequestBody ajax传递对象数组
- 韩拓-七牛产品演进之路
- 陶陶的兔二,建好啦!
- 【Asp.Net】Asp.Net CommandName作用
- 侠客博客v1.0 正式版版本发布
- SQLServer2k安全配置
- CodeForces - 1304D Shortest and Longest LIS(构造+贪心)
- CodeForces - 798D Mike and distribution(构造+思维/玄学随机数)