利用递归方法实现链表反转、前N个节点反转以及中间部分节点反转
一、反转整个链表
问题:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
//单链表的实现结构
public class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x;}
}
反转链表利用迭代不难实现,如果使用递归则有些许难度。
首先来看源码实现:
ListNode reverse(ListNode head) {
if(head == null || head.next == null)
return head;
ListNode ret = reverse(head.next);
head.next.next = head;
head.next = null;
return ret;
}
是否看起来不知所云,而又被这如此简洁的代码所震撼?让我们一起探索一下其中的奥秘。
对于递归算法,最重要的是明确递归函数的定义。
我们的reverse函数的定义如下:
输入一个节点head,将以head为起点的链表反转,并返回反转之后的头节点。

明白了函数的定义后,在来看这个问题。比如我们想反转这个链表

那么输入reverse(head)后,会在ListNode ret = reverse(head.next);进行递归
不要跳进递归!(你的脑袋能压几个栈呀?)
根据reverse函数的定义,函数调用后会返回反转之后的头节点,我们用变量ret接收
现在再来看一下代码
head.next.next = head;

接下来:
head.next = null;
return ret;

再跳出这层递归就会得到:

神不神奇,这样整个链表就反转过来了!
递归代码就是这么简洁优雅,但要注意两个问题:
1、递归函数要有base case,不然就会一直递归,导致栈溢出
if (head == null || head.next == null) return head;
即链表为空或只有一个节点,直接返回
2、当链表递归反转后,新的头节点为ret,而head变成了最后一个节点,应该令链表的某尾指向null
head.next = null;
理解这两个问题之后,我们可以进一步深入研究链表反转的问题,接下来的问题其实均为在这个算法上的扩展。
二、反转链表前N个节点
接下来我们来看这个问题:
问题:反转链表前N个节点,并返回链表头节点
说明:1 <= N <= 链表长度
示例:
输入: 1->2->3->4->5->NULL, n = 4
输出: 4->3->2->1->5->NULL
解决思路和反转整个链表差不多,只需稍加修改
ListNode successor = null; // 后驱节点(第 n + 1 个节点)
ListNdoe reverseN(ListNode head, int n) {
if (n == 1) {
successor = head.next;
return head;
}
// 以 head.next 为起点,需要反转前 n - 1 个节点
ListNode ret = reverseN(head.next, n - 1);
head.next.next = head;
head.next = successor; // 将反转后的 head 与后面节点连接
return ret;
}
具体区别:
1、base case 变为n == 1, 同时需要记录后驱节点。
2、之前把head.next 设置为null,因为整个链表反转后,head变为最后一个节点。
现在head节点在递归反转后不一定为最后一个节点,故应记录后驱successor(第 n + 1 个节点), 反转之后将head连接上。

OK,如果这个函数你也能看懂,就离实现反转一部分链表不远了。
三、反转链表的一部分
现在我们开始解决这个问题,给一个索引区间[m, n](索引从1开始),仅仅反转区间中的链表元素。
说明:1 <= m <= n <= 链表长度
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
猛一看很难想到思路。
试想一下,如果m == 1,就相当于反转链表的前 n 元素嘛,也就是我们刚才实现的功能:
ListNode reverseBetween(ListNode head, int m, int n) {
//base case
if (m == 1) {
return reverseN(head, n); // 相当于反转前 n 个元素
}
// ...
}
那如果m != 1 该怎么办?
如果把head的索引视为1,那么我们是想从第m个元素开始反转;
如果把head.next的索引视为1,那么我们是想从第m - 1 个元素开始反转;
如果把head.next.next的索引视为1,那么我们是想从第m - 2 个元素开始反转;
......
区别于迭代思想,这就是递归的思想,所以我们可以完成代码:
ListNode reverseBetween(ListNode head, int m, int n) {
// base case
if (m == 1) {
return reverseN(head, n);
}
// 递归前进到触发 base case (m == 1)
head.next = reverseBetween(head.next, m - 1, n - 1);
return head;
}
至此,我们终于干掉了大BOSS!
利用递归方法实现链表反转、前N个节点反转以及中间部分节点反转的更多相关文章
- 利用grunt-contrib-connect和grunt-connect-proxy搭建前后端分离的开发环境
前后端分离这个词一点都不新鲜,完全的前后端分离在岗位协作方面,前端不写任何后台,后台不写任何页面,双方通过接口传递数据完成软件的各个功能实现.此种情况下,前后端的项目都独立开发和独立部署,在开发期间有 ...
- 利用 C++ 单向链表实现队列
利用C++ 单向链表实现数据结构队列,其实和上一篇基本内容相同,仅仅是插入的时候在链表的尾部插入,取元素都是一样的,都从头部取. #pragma once #include "stdio.h ...
- 单链表的前K个的逆序输出
单链表逆序输出也是常被面试官问到题算法题,所以自己就总结了一下,在此贴出算法,与小伙伴们相互交流. 首先要有三个指针,前两个分别指向首节点,首节点的下一个节点,第三个是临时指针,是为了储存首节点的下一 ...
- 在单链表的第i个位置后插入一个节点(阿里+腾讯等面试题总结)
时间:2014.04.26 地点:基地 ------------------------- 一.题目 题目是非常easy和基础,就是在单链表的第i个位置后插入一个节点.要求写代码,5分钟之内完毕.面腾 ...
- Partition List(链表的插入和删除操作,找前驱节点)
Given a linked list and a value x, partition it such that all nodes less than x come before nodes gr ...
- 通过数据库中的表,使用 MyEclipse2017的反向生成工具-->hibernate反转引擎引擎(MyEclipse2017自带的插件) 来反转生成实体类和对应的映射文件
通过数据库中的表,使用 MyEclipse2017的反向生成工具-->hibernate反转引擎引擎(MyEclipse2017自带的插件) 来反转生成实体类和对应的映射文件 文章目录 Ja ...
- 利用BBED恢复UPDATE改动前的值
转载请注明出处:http://blog.csdn.net/guoyjoe/article/details/30615151 实验步骤例如以下: 1.创建表guo_test1 gyj@PROD> ...
- java 中递归的实现 以及利用递归方法实现汉诺塔
今天说下java语言中比较常见的一种方法,递归方法. 递归的定义 简单来说递归的方法就是"自己调用自己",通过递归方法往往可以将一个大问题简单化,最终压缩到一个易于处理的程度.对于 ...
- 利用快排partition求前N小的元素
求前k小的数,一般人的想法就是先排序,然后再遍历,但是题目只是求前N小,没有必要完全排序,所以可以想到部分排序,而能够部分排序的排序算法我能想到的就是堆排序和快排了. 第一种思路,局部堆排序. 首先, ...
随机推荐
- Proteus 8使用 1新建一个Proteus工程
新建一个Proteus工程 下一步 创建部分结束,可以看到两部分-->原理图与源代码. 首先按下F7或从“构建”菜单中选择“构建工程” 之后切换到原理图窗口 按下F12或点击窗口最左下角的“运行 ...
- 使用 Python 为女神挑选口红
口红对于女生来说永远不嫌多,而男生也搞不明白珊瑚红.番茄色.斩男色等等颜色有什么区别,不都是红色么?当送给女神的口红是她不适合的,那结果就是口红进入垃圾箱还算是轻的,重则拉黑处理.男生们也不用着急,我 ...
- java 模拟斗地主发牌洗牌
一 模拟斗地主洗牌发牌 1.案例需求 按照斗地主的规则,完成洗牌发牌的动作. 具体规则: 1. 组装54张扑克牌 2. 将54张牌顺序打乱 3. 三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张 ...
- C#设计模式之4-原型模式
原型模式(Prototype Pattern) 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/395 访问. 原型模式属 ...
- 编译hotspot8
编译hotspot8 ubuntu desktop 18 全新准备与编译过程再记录下: # 建议使用此gcc和g++版本,过高版本比如gcc7或引发编译报错 sudo apt-get install ...
- python3中文输出乱码的问题
最近使用you-get这个工具下载视频,发现命令行窗口里显示的媒体标题是乱码(但文件管理器里显示正常).我的命令行窗口的code page是936,sys.stdout.encoding是utf-8, ...
- three.js 利用uv和ThreeBSP制作一个快递柜
最近有three网友,问我要不要学习blender,其实我感觉学习一下也无妨,不过花大量时间精通,尚可不必,术业有专攻给别人留一条路吧,哈哈.那我我们就是用ThreeBSP和uv贴图的知识来制作一个定 ...
- Windows下nacos单机部分发现的坑
一.下载nacos的地址: https://github.com/alibaba/nacos/releases 下载 nacos-server-1.3.2.tar.gz 就好 二.在Window ...
- PythonCrashCourse 第六章习题
使用一个字典来存储一个熟人的信息,包括名.姓.年龄和居住的城市.该字典应包含键first_name .last_name .age 和city .将存储在该字典中 的每项信息都打印出来 person ...
- 大整数加法C++(计蒜客)
求两个不超过 200200 位的非负整数的和. 输入格式 有两行,每行是一个不超过 200200 位的非负整数,可能有多余的前导 00. 输出格式 一行,即相加后的结果.结果里不能有多余的前导 00, ...