🚩传送门:牛客题目

题目

给你单链表的头指针 head 和两个整数 left right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表

示例 1:
[NC]21. 链表内指定区间反转 - 图1

输入:head = [1,2,3,4,5], left = 2, right = 4 输出:[1,4,3,2,5]

示例 2:

输入:head = [5], left = 1, right = 1 输出:[5]

解1:先反转区间再拼接

算法步骤:
第 1 步:先将待反转的区域反转;
第 2 步:prenext 指针指向反转以后的链表头节点,把反转以后的链表的尾节点的 next 指针指向 succ
image.png

复杂度分析

时间复杂度:[NC]21. 链表内指定区间反转 - 图3,其中 [NC]21. 链表内指定区间反转 - 图4 是链表总节点数。最坏情况下,需要遍历整个链表。

空间复杂度:[NC]21. 链表内指定区间反转 - 图5,只使用到常数个变量。

我的代码

  1. class Solution {
  2. public ListNode reverseBetween(ListNode head, int left, int right) {
  3. // 因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论
  4. ListNode dummyNode = new ListNode(-1);
  5. dummyNode.next = head;
  6. ListNode pre = dummyNode;
  7. // 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
  8. // 建议写在 for 循环里,语义清晰
  9. for (int i = 0; i < left - 1; i++) {
  10. pre = pre.next;
  11. }
  12. // 第 2 步:从 pre 再走 right - left + 1 步,来到 right 节点
  13. ListNode rightNode = pre;
  14. for (int i = 0; i < right - left + 1; i++) {
  15. rightNode = rightNode.next;
  16. }
  17. // 第 3 步:切断出一个子链表(截取链表)
  18. ListNode leftNode = pre.next;
  19. ListNode curr = rightNode.next;
  20. // 注意:切断链接
  21. pre.next = null;
  22. rightNode.next = null;
  23. // 第 4 步:同第 206 题,反转链表的子区间
  24. reverseLinkedList(leftNode);
  25. // 第 5 步:接回到原来的链表中
  26. pre.next = rightNode;
  27. leftNode.next = curr;
  28. return dummyNode.next;
  29. }
  30. private void reverseLinkedList(ListNode head) {
  31. // 也可以使用递归反转一个链表
  32. ListNode pre = null;
  33. ListNode cur = head;
  34. while (cur != null) {
  35. ListNode next = cur.next;
  36. cur.next = pre;
  37. pre = cur;
  38. cur = next;
  39. }
  40. }
  41. }

解1:一次遍历反转

思想是:在需要反转的区间里,每遍历到一个节点,让这个新节点来到反转部分的起始位置。

下面的图展示了整个流程。
image.png

复杂度分析

时间复杂度:[NC]21. 链表内指定区间反转 - 图7,其中 [NC]21. 链表内指定区间反转 - 图8 是链表总节点数。最坏情况下,只需要遍历一次链表。

空间复杂度:[NC]21. 链表内指定区间反转 - 图9,只使用到常数个变量。

我的代码

  1. class Solution {
  2. public ListNode reverseBetween(ListNode head, int left, int right) {
  3. // 设置 dummyNode 是这一类问题的一般做法
  4. ListNode dummyNode = new ListNode(-1);
  5. dummyNode.next = head;
  6. ListNode pre = dummyNode;
  7. for (int i = 0; i < left - 1; i++) {
  8. pre = pre.next;
  9. }
  10. ListNode cur = pre.next;
  11. ListNode next;
  12. for (int i = 0; i < right - left; i++) {
  13. next = cur.next;
  14. cur.next = next.next;
  15. next.next = pre.next;
  16. pre.next = next;
  17. }
  18. return dummyNode.next;
  19. }
  20. }