本文是关于单链表的反转问题, 存在反转全部链表和部分区间链表的情况.
涉及 leetcode 的206. 反转链表和92. 反转链表 II.
反转链表
题目1
首先来看题目206. 反转链表: 给你单链表的头节点 head , 请你反转链表, 并返回反转后的链表.
示例1:
输入: head = [1,2,3,4,5]
输出: [5,4,3,2,1]
示例2:
输入: head = []
输出: []
理解一下1
反转很好理解, 链表本来是指向其唯一后继, 现在只不过是去指向其唯一前趋.
基本的思路是:
- 需要三个指针, 一个指向前趋, 一个指向当前结点, 一个指向后继;
- 然后循环迭代, 变换指针的移动即可.
以上的是最基本的迭代思维, 那么尝试使用递归的思想来解决呢?
- 首先递归反转某个结点后的所有链表;
- 直至base case: 空链或唯一结点;
- 然后将反转后的链表与该结点连接上即可.
解题1
首先是迭代的算法:
1 | ListNode* reverseList(ListNode* head) { |
然后是递归的解法, 最后三行不好理解的话画个图就知道了:
1 | ListNode* reverseList(ListNode* head) { |
反转部分链表
题目2
首先来看题目92. 反转链表 II: 给你单链表的头指针 head 和两个整数 left 和 right, 其中 left <= right . 请你反转从位置 left 到位置 right 的链表节点, 返回反转后的链表.
示例1:
输入: head = [1,2,3,4,5], left = 2, right = 4
输出: [1,4,3,2,5]
示例2:
输入: head = [5], left = 1, right = 1
输出: [5]
理解一下2
与上一题不同的是, 这题需要将链表的部分区间进行翻转, 这就需要将链表分为三个部分处理: 前部分-翻转部分-后部分, 这会显得比较麻烦.
那么我们考虑一个问题: 反转链表前N个结点, 那么链表就只需要分为: 翻转部分-后部分, 相比而言简单一些了.
反转前N个结点的基本的思路是:
- 通过N设置对反转结束的判断, 同时需要一个指针来表示后继;
- 与第一题一样翻转;
那么对于我们的区间就很简单了:
- 不断递归或迭代后移至需要反转的结点;
- 然后不就是反转链表前N个结点的问题嘛!
解题2
由于结点和指针相对较多, 要仔细判断清楚.
1 | ListNode* reverseBetween(ListNode* head, int left, int right) { |
个人收获
在这里反转链表的问题上, 迭代和递归的时间复杂度都是O(n), 所以选择使用哪个都差不多, 但是递归的空间复杂度却是O(n), 所以实际应用的时候还是迭代较好.
不过这里让我对递归有了更好的认知, 写递归函数, 就是要相信这个递归函数可以做到它该做的, 然后我们将base case
和其他需要准备的做好就可以了, 剩下的就相信它!