【代码随想录 | day04】(JavaScript) 链表其他操作

123 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

  • 24.两两交换链表中的节点
  • 19.删除链表的倒数第N个节点
  • 面试题 02.07. 链表相交
  • 142.环形链表II

该博客内容参考Carl老师的《代码随想录》,记录一些自己的所思所想,以及卡神的解题思路。

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

题目链接:24. 两两交换链表中的节点 - 力扣(LeetCode)

错误想法

错误想法.jpg

建议使用虚拟头结点,这样会方便很多,要不然每次针对头结点(没有前一个指针指向头结点),还要单独处理。

我的思路以及瓶颈

题目给的测试用例是:head = [1,2,3,4]

  • 那么现在有一个问题就是说,如何将cur定位到2这里?
  • 注意到18行,说明17行中虚拟头结点的设置成功。现在的链表为:0(虚拟)➡️1➡️2➡️3➡️4
  • 因为要两两交换,所以先设置一个 len 方便后续使用。设置临时指针cur=dummyNode,那么可以看到cur.next就是原来的链表了! 1120_1.jpg
  • 接下来就是不断移动cur指针,当cur指针指向 2 的时候,来个“乾坤大挪移”! 挪移.jpg 可以看到26行中,cur打印的结果就是[2,3,4]。说明cur现在指向的就是 2 。
  • temp 用来暂时存一下2.next之前的指针,这样指针改变后,原来指向3的地址就不会丢失。
  • dummyNode.next.next = temp;就是将 1 的指针指向 3.
  • 瓶颈: 目前只能交换头部两个节点。然后链表的长度貌似也打印不出来。dummyNode.length 输出的还是 undefined。

卡神解法

链接:代码随想录 (programmercarl.com)

 var swapPairs = function (head) {
   let ret = new ListNode(0, head), temp = ret;
   while (temp.next && temp.next.next) {
     let cur = temp.next.next, pre = temp.next;
     pre.next = cur.next;
     cur.next = pre;
     temp.next = cur;
     temp = pre;
   }
   return ret.next;
 };
  • 运行let ret = new ListNode(0, head), temp = ret;过后,目前temp就是[0, 1, 2, 3, 4]

  • let cur = temp.next.next, pre = temp.next;

    解读:这个cur指向了2pre指向了1

  • pre.next = cur.next;

    解读:让 1 指向 2.next ,即:1 ➡️ 3

  • cur.next = pre;

    解读:cur还是2, 2 ➡️ 1 。

  • temp.next = cur;

    解读:那么得让虚拟头结点指向2 。则temp就是[0, 2, 1, 3, 4]

  • temp = pre;

    解读:如果不写这一句,那么执行结果会超出时间限制。原因:如果没有这一句,那么本次循环终止,进入下次循环,那么 temp.next 和 temp.next.next 又变成了 2 和 1 。那么就会无限循环下去。⭐️而使用了 temp = pre ,pre之前就是 数值为 1 的这个节点,相当于下次循环时,1.next 和 1.next.next 就变成了 3和4 。实在是妙哇!!


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

题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

双指针的操作,要注意,删除第N个节点,那么我们当前遍历的指针一定要指向 第N个节点的前一个节点

卡神解法

  1. 这道题目的关键是要找到倒数第n个节点
  2. 快指针先移动n步,然后快慢指针再同时移动,直到快指针指向了null。那么时候slow指针指向的就是我们要删除的那个节点。
  3. 但是实际上,我们一定要让指针指向要删除的节点的前一个节点。这样,前一个节点➡️要删除节点的next,就能完成删除节点的操作。
  4. 所以,快指针要多走一步,然后快慢指针再同时移动
 var removeNthFromEnd = function(head, n) {
     let ret = new ListNode(0, head), // 创建虚拟头结点
         slow = fast = ret;
     while(n--) fast = fast.next;
     while (fast.next !== null) {
         fast = fast.next; 
         slow = slow.next
     };
     slow.next = slow.next.next;
     return ret.next;
 };

结合测试用例:[1,2,3,4,5] , n = 2 来解释一下代码步骤

创建虚拟头结点后,此时的 slow = fast = ret 都为 [0, 1, 2, 3, 4, 5]

  • while(n--) fast = fast.next;

    解读:此时快指针走了 n 步数,

  • fast = fast.next;

    解读:实际上要求 fast 走 n+1 步数,所以在while (fast.next !== null)中,fast 又走了一步

  • 自己画画图也可以得知,进过 while 循环过后,此时的 slow 指向了要删除的节点的前一个节点。在用例中就是指向了 3 。

  • slow.next = slow.next.next; 只要让 3 ➡️ 5 ,那么就能删除4 。


面试题 02.07. 链表相交

题目链接:面试题 02.07. 链表相交 - 力扣(LeetCode)

image-20221120192736004.png

卡神解法

注意点:交点不是数值相等,而是指针相等。

首先需要知道链表的长度,由于链表和数组不同,不能通过了,length属性求出长度。所以需要自己写一个求长度的函数。

 var getListLen = function(head) {
     let len = 0, cur = head;
     while(cur) {
        len++;
        cur = cur.next;
     }
     return len;
 }
  • 其中还是需要一个临时指针 cur ,cur = head ,用于后面遍历链表。

其次,求出两个链表的长度,并求出两个链表长度的差值。然后让curA移动到,和curB 末尾对齐的位置

 var getIntersectionNode = function(headA, headB) {
     let curA = headA,curB = headB,
         lenA = getListLen(headA),
         lenB = getListLen(headB);
     if(lenA < lenB) {
         // 下面交换变量注意加 “分号” ,两个数组交换变量在同一个作用域下时
         // 如果不加分号,下面两条代码等同于一条代码: [curA, curB] = [lenB, lenA]
         [curA, curB] = [curB, curA];
         [lenA, lenB] = [lenB, lenA];
     }
     let i = lenA - lenB;
     while(i-- > 0) {
         curA = curA.next;
     }
     while(curA && curA !== curB) {
         curA = curA.next;
         curB = curB.next;
     }
     return curA;
 };
  • 长度的差值需要大于 0 ,所以通过 if 后面的语句段,让 lenA 设为长度更长的链表。

  • while(i-- > 0) { curA = curA.next; }

    解读:让 curA 移动到,和 curB 末尾对齐的位置(借用一下大佬的图) !

    链表相交2.jpg

  • while(curA && curA !== curB)

    解读:如果 curA 现在所指不为空,而且和 curB 不相等。那么两个指针继续移动一格。


142.环形链表II

题目链接:142. 环形链表 II - 力扣(LeetCode)

 // 两种循环实现方式
 /**
  * @param {ListNode} head
  * @return {ListNode}
  */
 // 先判断是否是环形链表
 var detectCycle = function(head) {
     if(!head || !head.next) return null;
     let slow =head.next, fast = head.next.next;
     while(fast && fast.next && fast!== slow) {
         slow = slow.next;
         fast = fast.next.next; 
     }
     if(!fast || !fast.next ) return null;
     slow = head;
     while (fast !== slow) {
         slow = slow.next;
         fast = fast.next;
     }
     return slow;
 };
 ​
 var detectCycle = function(head) {
     if(!head || !head.next) return null;
     let slow =head.next, fast = head.next.next;
     while(fast && fast.next) {
         slow = slow.next;
         fast = fast.next.next;
         if(fast == slow) {
             slow = head;
             while (fast !== slow) {
                 slow = slow.next;
                 fast = fast.next;
             }
             return slow;
         }
     }
     return null;
 };