代码随想录|day5 链表part2

71 阅读3分钟

24. 两两交换链表中的节点

题目链接:leetcode.cn/problems/sw…

思路:

两两交换节点 [1,2,3,4]。先更改指向 1 -> 3,再2 -> 1,最后 1->3

image.png

重点:

更改指向时,如何不切断整个链表的链路??

  • 需临时记录两两交换节点中第二个节点的前一个节点和后一个节点,例子中就是记录 1、3。

代码:

var swapPairs = function (head) {
    let dummyHead = new ListNode(0, head)
    let cur = dummyHead
    while (cur.next != null && cur.next.next != null) {
        const temp1 = cur.next
        temp2 = cur.next.next.next
        cur.next = cur.next.next
        temp1.next = temp2
        cur.next.next = temp1
        cur = cur.next.next
    }
    return dummyHead.next
};

19. 删除链表的倒数第N个节点

题目链接:leetcode.cn/problems/re…

思路: 双指针又派上用场了!

定义快慢指针(slow),快指针(fast)先走 n 步,后快慢指针再同步走。当fast指向 null 的之后,slow的指向就为我们要删除的节点。

重点:

实现时快指针走 n+1 。why?!

  • 因为要删除倒数第 n 个节点,就需要操作他的前一个节点,那怎么获取前一个节点呢!就需要让fast走 n+1 步,当 fast 等于 null时slow指向就是要删除节点的前一个节点了。
  • fast走 n+1 带入到理论中就可以理解为fast为 null 的时候,slow往前移了一个位置

代码:

var removeNthFromEnd = function (head, n) {
    // 双指针、快指针先走 n,慢指针指向要删除的节点
    let dummyHead = new ListNode(0, head)
    let slow = dummyHead, fast = dummyHead
    while (n-- >= 0 && fast != null) {
        fast = fast.next
    }
    while (fast != null) {
        fast = fast.next
        slow = slow.next
    }
    slow.next = slow.next.next
    return dummyHead.next
};

面试题 02.07. 链表相交

题目链接:leetcode.cn/problems/in…

思路:

遍历两个链表分别记录其长度,计算长度差 gap。长链表先走 gap 值,让长短链表尾部对齐。长短链表同时遍历,长链表起始节点则为第 gap 个节点,短链表起始节点为头节点,当找到相同节点返回当前节点,否则返回null

代码:

var getIntersectionNode = function (headA, headB) {
    let lenA = 0,
    lenB = 0,
    curA = headA,
    curB = headB;
  while (curA != null) {
    lenA++;
    curA = curA.next;
  }
  while (curB != null) {
    lenB++;
    curB = curB.next;
  }

  curA = headA;
  curB = headB;
  if (lenA < lenB) {
    [lenA, lenB] = [lenB, lenA];
    [curA, curB] = [curB, curA];
  }
  let diff = lenA - lenB;
  while (diff--) {
    curA = curA.next;
  }
  while (curA != null) {
    if (curA === curB) {
      return curA;
    }
    curA = curA.next;
    curB = curB.next;
  }
  return null;
};

142. 环形链表II

题目链接:leetcode.cn/problems/li…

思路:

双指针又来喽! 快慢指针(fast、slow),让快指针走两步、慢指针走一步。如果链表有环则快指针先入环。慢指针入环后一块内快指针必追上慢指针,快慢指针重合后如何找到环入口?请看以下重点

重点:

怎样判断链表有环?

  • 快慢指针,fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇,这是毋庸置疑的

为什么fast指针和slow指针一定会相遇?

  • 相对于slow来说,fast是一个节点一个节点的靠近slow的
  • 动画如下:

141.环形链表

快慢指针相遇后环入口怎么找?

  • 因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:(x + y) * 2 = x + y + n (y + z)
  • 整理公式:x = (n - 1) (y + z) + z。当 n=1 时,x = z

代码:

var detectCycle = function (head) {
    let fast = head, slow = head
    while (fast != null && fast.next != null) {
        fast = fast.next.next
        slow = slow.next
        if (fast === slow) {
            let index1 = head, index2 = fast
            while (index1 !== index2) {
                index1 = index1.next;
                index2 = index2.next
            }
            return index1
        }
    }
    return null
};

总结

嘿嘿。为什么直接到day5了,不告诉你🫣

链表章节至此告一段落,让我们来总结下链表算法的重点吧~

1、什么情况下使用虚拟头节点?

虚拟头节点的好处就是我们可以按照同一种逻辑来处理每个节点。

修改节点指向可以应用虚拟头节点。如只做查找操作可以不使用虚拟头节点

2、含有修改指向操作的cur指向哪个节点?

cur应该指向我们需要操作节点的前一个节点,因为当前题目前提都是单向链表,如果将cur指向需要操作的节点,如删除操作,那我们是拿不到前一个节点的。