- L206 反转链表
- 题目描述
- 题解
- 方法一:迭代
- 方法二:递归
- L92 反转链表 II
- 题目描述
- 题解
- 方法一:一遍扫描
- 方法二:穿针引线
- L25 K个一组反转链表
- 题目描述
- 题解
- 方法一:模拟
L206 反转链表
题目描述
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例1:
示例2:
题解
方法一:迭代
假设链表为 1→2→3→∅,我们想要把它改成 ∅←1←2←3。
我们考虑遍历链表,将每个节点的next指针改为指向前一个节点。但是链表是单向的,当遍历到某个节点时,我们已经没有前一个节点的引用了,所以需要使用一个prev引用来保存当前节点的前一个节点。
然后做以下迭代步骤:
- 保存curr的next引用
- 设置当前节点的next指向prev
- 将prev指向当前节点
- 将curr指向next
class Solution {public ListNode reverseList(ListNode head) {ListNode curr = head;ListNode prev = null;while (curr != null) {ListNode next = curr.next;curr.next = prev;prev = curr;curr = next;}return prev;}
}
复杂度分析
时间复杂度:O(N),遍历一次。
空间复杂度:O(1)
方法二:递归
假设链表为:
n1→…→n**k−1→n**k→n**k+1→…→n**m→∅
若从节点 n**k+1 到 n**m 已经被反转,而我们正处于 n**k
n1→…→n**k−1→n**k→n**k+1←…←n**m
我们希望 n**k+1 的下一个节点指向 n**k。
所以,n**k.next.next=n**k。
需要注意的是:
- next指针的修改
- 反转后的链表的链尾节点的next应该为null
- 在递归过程中应该要将反转后的链表头节点返回
class Solution {public ListNode reverseList(ListNode head) {if (head == null || head.next == null) {return head;}ListNode newHead = reverseList(head.next);head.next.next = head;head.next = null;return newHead;}
}
复杂度分析
时间复杂度:O(N)
空间复杂度:O(N),因为递归占用栈空间
L92 反转链表 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]
题解
方法一:一遍扫描
将链表分成三段,反转中间的部分,然后将左中右重新相接。
class Solution {public ListNode reverseBetween(ListNode head, int left, int right) {ListNode dummy = new ListNode(0, head);// 0.整个链表分为左中右三段// 1.首先找到左链表的尾部ListNode node = dummy;for (int i = 1; i < left; i++) {node = node.next;}ListNode leftListTail = node;// 2.反转中间链表node = node.next;ListNode midListHead = node;ListNode prev = null;for (int i = left; i <= right; i++) {ListNode next = node.next;node.next = prev;prev = node;node = next;}// 3.重新相接leftListTail.next = prev;midListHead.next = node;return dummy.next;}
方法二:穿针引线
pre永远指向左链表的最后一个节点。
curr永远指向右链表的第一个节点。
每次迭代:
- 先将
curr
的下一个节点记录为next
- 执行操作 ①:把
curr
的下一个节点指向next
的下一个节点 - 执行操作 ②:把
next
的下一个节点指向pre
的下一个节点 - 执行操作 ③:把
pre
的下一个节点指向next
class Solution {public ListNode reverseBetween(ListNode head, int left, int right) {// 设置 dummyNode 是这一类问题的一般做法ListNode dummyNode = new ListNode(-1);dummyNode.next = head;ListNode pre = dummyNode;for (int i = 0; i < left - 1; i++) {pre = pre.next;}ListNode cur = pre.next;ListNode next;for (int i = 0; i < right - left; i++) {next = cur.next;cur.next = next.next;next.next = pre.next;pre.next = next;}return dummyNode.next;}
}
L25 K个一组反转链表
题目描述
给你链表的头节点 head
,每 k
个节点一组进行翻转,请你返回修改后的链表。
k
是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k
的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例1
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
示例2
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
题解
方法一:模拟
思路就是先向后遍历k个节点,如果剩余元素个数少于k个,则直接返回;如果大于等于k个,就进行翻转。
反转完成之后,需要重新将它接回原链表。
class Solution {public ListNode reverseKGroup(ListNode list, int k) {ListNode dummy = new ListNode(0, list);ListNode tail = dummy;while (true) {ListNode curr = tail;for (int i = 0; i < k; i++) {curr = curr.next;if (curr == null) {return dummy.next;}}ListNode nextKGroupHead = curr.next;ListNode currKGroupHead = tail.next;ListNode prev = nextKGroupHead;curr = tail.next;while (curr != nextKGroupHead) {ListNode nxt = curr.next;curr.next = prev;prev = curr;curr = nxt;}tail.next = prev;tail = currKGroupHead;}}
}