目录
LeetCode025K 个一组翻转链表
解题思路
解法一
解法二
代码实现
解法一
解法二
LeetCode逆序相关题目
链表反转
排序奇升偶降链表
LeetCode025K 个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例:
给你这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
说明:
你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换
解题思路
解法一
1、 先反转以 head 开头的 k 个元素。
2、 将第 k + 1 个元素作为 head 递归调⽤ reverseKGroup 函数。
3、如果最后的元素不⾜ k 个, 就保持不变
解法二
看图写代码
1.为了代码的可读性,定义一个虚拟指针dummy作为head的前驱指针
2.初始需要两个变量 pre
和 end
,pre
代表待翻转链表的前驱,end
代表待翻转链表的末尾
3.start和next是while循环内的局部变量分别对应 pre
和 end的后继指针
代码实现
解法一
先要理解下单链表的逆序算法:漫画:如何将一个链表“逆序”? - 知乎,然后理解如下代码
package com.lzhsite.leetcode.algoritom.practise.linked;
import com.lzhsite.leetcode.algoritom.dataStruct.DLNode;
public class LeetCode025K个一组翻转链表 {
/** 反转区间 [a, b) 的元素, 注意是左闭右开 */
DLNode reverse(DLNode a, DLNode b) {
ListNode p1, p2, p3;
p1 = a;
p2 = a.next;
p3 = null;
// while 终⽌的条件改⼀下就⾏了
while (p2 != b) {
p3 = p2.next;
p2.next = p1;
p1 = p2;
p2 = p3;
}
// 返回反转后的头结点
return p1;
}
DLNode reverseKGroup(DLNode head, int k) {
if (head == null)
return null;
// 区间 [a, b) 包含 k 个待反转元素
DLNode a, b;
a = b = head;
for (int i = 0; i < k; i++) {
// 不⾜ k 个, 不需要反转, base case
if (b == null)
return head;
b = b.next;
}
// 如何k个⼀组反转链表
// 反转前 k 个元素
DLNode newHead = reverse(a, b);
// 递归反转后续链表并连接起来
a.next = reverseKGroup(b, k);
return newHead;
}
}
解法二
public ListNode reverseKGroup(ListNode head, int k) {
if (head == null || k == 1) {
return head;
}
ListNode curr = head;
int count = 0;
while (curr != null && count != k) { // 找到第 k+1 个节点
curr = curr.next;
count++;
}
if (count == k) { // 如果有 k 个节点,则翻转
ListNode pre = reverseKGroup(curr, k); // 递归连接后续翻转的链表,返回值pre是翻转后链表的头结点
while (count-- > 0) { // 翻转当前 k 个节点
ListNode tmp = head.next; // 临时节点,用于暂存下一个节点
head.next = pre; // 把当前节点指向上一个节点
pre = head; // 更新 curr,用于记录上一个节点
head = tmp; // 把备份还给head相当于移动到下一个节点,后续继续执行逆序操作
}
head = pre; // 这一行在完成 k 个节点的反转后更head始终指向头结点
}
return head;
}
LeetCode逆序相关题目
链表反转
思考: 比较下单链表逆序的递归写法,思考reverseKGroup逆序时为什么不需要断开原来的指向
因为reverseKGroup操作的是当前节点实现逆序已经覆盖了原来的节点。reverseList操作的是后续节点,原来的节点的下一个没被覆盖。
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;
}
}
LeetCode92. 反转链表 II
给你单链表的头指针 head
和两个整数 left
和 right
,其中 left <= right
。请你反转从位置 left
到位置 right
的链表节点,返回 反转后的链表 。
示例 :
输入:head = [1,2,3,4,5], left = 2, right = 4 输出:[1,4,3,2,5]
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if (head == null || left == right) {
return head;
}
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode prev = dummy;
for (int i = 0; i < left - 1; i++) {
prev = prev.next;
}
// 保存 rightNode 的下一个节点
ListNode rightNode = prev;
for (int i = 0; i < right - left + 1; i++) {
//从 prev 节点开始,向前移动 right - left + 1 步来定位到 right 位置的节点(
rightNode = rightNode.next;
}
//rightNode 的下一个节点
ListNode tailNext = rightNode.next;
// 反转部分链表,并获取反转部分的新头节点
ListNode[] reversed = reverseRecursively(prev.next, right - left + 1);
// 重新连接反转后的链表
prev.next = reversed[0];
reversed[1].next = tailNext;
return dummy.next;
}
排序奇升偶降链表
- 题目
给定一个奇数位升序,偶数位降序的链表,将其重新排序。
输入: 1->8->3->6->5->4->7->2->NULL 输出: 1->2->3->4->5->6->7->8->NULL
- 思路
1. 按奇偶位置拆分链表,得1->3->5->7->NULL和8->6->4->2->NULL
2. 反转偶链表,得1->3->5->7->NULL和2->4->6->8->NULL
3. 合并两个有序链表,得1->2->3->4->5->6->7->8->NULL
第2步和第3步分别对应的力扣206. 反转链表和21. 合并两个有序链表,而第1步的解法与328. 奇偶链表差不多。如果搞懂这3道leetcode,那么本篇文章的这道题肯定不在话下了。
- 代码
class Solution {
public ListNode oddEvenList(ListNode head) {
if (head == null) return null;
ListNode odd = head, even = head.next, evenHead = even;
while (even != null && even.next != null) {
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
odd.next = null; // cut off the odd list
return mergeTwoLists(head, reverse(evenHead));
}
private ListNode reverse(ListNode head) {
ListNode prev = null;
while (head != null) {
ListNode nextNode = head.next;
head.next = prev;
prev = head;
head = nextNode;
}
return prev;
}
private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}