代码随想录算法训练营第四天|链表part02

108 阅读3分钟

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

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

第一想法

没思路,去看卡哥视频

思路

image-20230319150459563

  • 循环条件的判断:

    注意,这里的节点数的奇数和偶数情况下,判断的条件不一样

    节点数是奇数时,循环的条件是 cur.next.next != null;

    节点数是偶数时,循环的条件是 cur.next != null;

    一开始本来想用分情况,即while(奇数情况||偶数情况)

    但是,这里有一个很方便的办法!!!

    while(cur.next ! = null && cur.next.next ! = null)

    利用了与操作的短路特性!!

  • 交换顺序

    注意,cur指向2时,cur和1的连接已经断了,此时找不到cur1,所以需要一个临时变量来存储cur1

    同理,2指向1时,2到3的连接也断了,需要一个临时变量存储3

  • 因为这里有虚拟头结点,所以不需要单独考虑链表为空或者里面只有一个元素的情况

JS 代码如下:

var swapPairs = function(head) {
    let dummyhead = new ListNode(0, head);
    let cur = dummyhead;
    while(cur.next != null && cur.next.next != null){
        let temp = cur.next;
        let temp1 = temp.next.next;
        // 注意这里的交换逻辑!
        cur.next = cur.next.next;
        cur.next.next = temp;
        temp.next = temp1;
        cur = cur.next.next;
    }
    return dummyhead.next;
};

总结

这个思路很重要

在考虑的过程中,要考虑不要让指针空指,即需要避免出现null.next的情况

交换逻辑可以一边画图一边写,每建立一条新的连接就把对应的旧的连接×掉,整个过程就会清晰得多

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

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

第一想法

先遍历链表,得出链表的长度

再用长度-n,定位到带删除节点的前一个节点,再进行删除

思路

快慢指针

让快指针先走n+1步

然后快慢指针同时移动

当快指针指向null时

慢指针指向待删除节点的前一个节点

JS代码如下:

var removeNthFromEnd = function(head, n) {
    let dummyhead = new ListNode(0,head);
    let fast = dummyhead,slow = dummyhead;
    
    for(i = 0; i < n+1; i++){
        fast = fast.next;
    }
    while(fast != null){
        fast = fast.next;
        slow = slow.next;
    }
    slow.next = slow.next.next;
    return dummyhead.next;
};

总结

这一题的思路很有意思

把双指针的思想用在链表中

160. 链表相交

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

第一想法

没有,想半天没思路

思路

注意链表的长度,两个链表的长度是有差异的

求出两个链表的长度,再把两个链表底端对齐

image-20230319155602642

因此,解题步骤为:

  • 获取两条链表长度,作差
  • 找到更长的一条,移动指针使其底端对齐
  • 两个指针开始同时移动,直到找到相同的节点

JS 代码如下:

var getIntersectionNode = function(headA, headB) {
    let curA = headA;
    let curB = headB;
    let sizeA = 0, sizeB = 0;
    // 获取链表长度
    sizeA = getlength(headA);
    sizeB = getlength(headB);
    // 找到更长的链表,让A是更长的列表
    if(sizeB > sizeA){
        [sizeA,sizeB] = [sizeB,sizeA];
        [curA,curB] = [curB,curA];
    }
    let n = sizeA - sizeB;
    for(i=0; i<n; i++){
        curA = curA.next;
    }
    // 此时两个指针可以同时移动
    while(curA != null && curB != null){
        if(curA === curB){
            return curA;
        }
        curA = curA. next;
        curB = curB.next;
        
    }
    return null;
​
};
function getlength(head){
    let len = 0, cur = head;
    while(cur != null){
        cur = cur.next;
        len++;
    }
    return len;
};

总结

这一题实际上写的时候还是看了看答案

因为找到更长的链表并且作差这个步骤一时半会没想到好的解决办法

然后看了看答案发现可以直接确定A为更长的

JS中可以在数组中存储对象,因此,这里交换就非常简单,直接用数组来交换

这一题的思路还挺重要,主要是要想清楚一点:如果两个链表有重合地方,则将它们底端对其的话

相同的节点会出现在相同的位置

142.环形链表II

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

第一想法

想半天没思路,去看视频

思路

快慢指针

快指针每次移动两个节点,慢指针每次移动一个节点

如果有环,他们一定会在环里相遇

如果没有环,则它们不可能相遇

image-20230319164904450

列出方程:

(x+y)*2 = x+y + n(y+z)

可得:

x = (n - 1) (y + z) + z

因为n是大于等于1

所以,如果在相遇点再出发一个指针index1,同时head再出发一个速度相同的指针index2,两个指针的交点就是环形的入口!

JS代码如下:

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 = fast;
            let index2 = head;
            // 找到了交点就出发新的指针
            while(index1 != index2){
                index1 = index1.next;
                index2 = index2.next;
                
            }
            return index1;           
            
        }
    }
    return null;
};

总结

虽然一开始有想过双指针法,不过没有想明白怎么用双指针

这题看了卡哥的视频还是很清晰的

思路需要好好的记一下