前言
反转链表相关的问题,在面试中出显示频率很高, 因此总结下几道链表反转相关的问题
- NC78 反转链表(简单)
- NC21 链表内指定区间反转(中等)
- NC50 链表中的节点每k个一组翻转(中等)
1. 反转链表
描述 : 输入一个链表,反转链表后,输出新链表的表头。
思路分析:
- 迭代实现 使用一个前置节点 pre 和 当前节点 cur 循环遍历更新即可
- 递归实现 定义递归函数,处理 base case , 对当前节点的 next 节点调用递归函数处理即可
AC 代码:
迭代版本: 时间复杂度 O(n) 空间复杂度 O(1)
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode pre = null;
ListNode next;
while (head != null) {
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
递归版本: 时间复杂度 O(n) 空间复杂度 O(n)
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode res = reverseList(head.next);
head.next.next = head;
head.next = null;
return res;
}
2. 链表内指定区间反转
描述 : 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
思路分析: 首先遍历 链表找到 left 位置的 start 节点 和 right + 1 位置的 tail 节点, 修改下 反转链表I 中的终止条件即可, 记录 left 的前置节点 leftPreNode , 最后连接到一起
Tip : 链表相关的问题 一般情况下都会添加一个虚拟的头节点;
下面实现代码中,将寻找指定位置节点的代码 和 反转指定区间节点的代码 单独放到了一个方法中, 实际上一次遍历可以完成这道题目, 处于可读性的考虑,将其中的逻辑抽出
AC 代码:
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode dumpNode = new ListNode(-1);
dumpNode.next = head;
ListNode leftPreNode = findNode(dumpNode, left);
ListNode leftNode = leftPreNode.next;
ListNode rightNextNode = findNode(dumpNode, right + 2);
leftPreNode.next = reverse(leftNode, rightNextNode);
leftNode.next = rightNextNode;
return dumpNode.next;
}
ListNode findNode(ListNode head, int index) {
int count = 1;
while (count != index) {
head = head.next;
count++;
}
return head;
}
ListNode reverse(ListNode head, ListNode tail) {
if (head == tail || head.next == tail) return head;
ListNode pre = null;
ListNode next;
while (head != tail) {
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
3. 链表中的节点每k个一组翻转
描述 :将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表; 如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样; 你不能更改节点中的值,只能更改节点本身。
输入: head = [1,2,3,4,5], k = 3
输出: [3,2,1,4,5]
思路分析: 使用 链表内指定区间反转 的 reverse(ListNode head, ListNode tail) 函数辅助; 定义递归函数 reverseKGroup 返回 反转之后的头节点, 反转一个区间之后 将该区间的尾节点指向 下一区间的头节点即可
AC 代码:
public ListNode reverseKGroup (ListNode head, int k) {
ListNode cur = head;
int count = 0;
while (cur != null) {
cur = cur.next;
count++;
if (count == k) {
break;
}
}
if (count != k) {
return head;
}
ListNode ans = reverse(head, cur);
head.next = reverseKGroup(cur, k);
return ans;
}