代码随想录day4 - 24. 两两交换链表中的节点 19.删除链表的倒数第N个节点 160.链表相交 142.环形链

71 阅读4分钟

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

力扣题目链接

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

image.png


个人思路

创建虚拟头结点dHead,从虚拟头结点开始遍历链表。

利用 next 变量来存储当前 node 节点后的第三个节点,将 node 后的第一个节点与第二个节点交换位置,并将next 的值赋值给新的node.next.next的next。

原理易于理解,但是操作可能有一点绕。

var swapPairs = function(head) {
    let dHead = new ListNode(0,head)
    let node = dHead

    while(node.next && node.next.next){
        let next = node.next.next.next
        let tmp = node.next
        node.next = node.next.next
        node.next.next = tmp
        node.next.next.next = next
        node = node.next.next
    }

    return dHead.next
};

代码随想录题解思路和个人思路基本相同,过。

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

力扣题目链接

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

image.png

解题思路

思路一,先遍历链表取得链表的长度,然后在从头开始,遍历到倒数第 n 个节点之前,然后通过目标节点前一个节点的next 指向来删除这个节点。

这个思路比较容易想到,也很好操作。

思路二,使用两个指针,分别为快慢指针,让快指针先走n步,然后再让快慢指针同步向前遍历。当快指针到达链表末尾时,慢指针正好到要删除的节点的前面。通过修改慢指针的指向,可以删除节点。

var removeNthFromEnd = function(head, n) {
    let slow = new ListNode(0,head)
    let ret = slow
    let fast = head
    while(n--){
        fast = fast.next
    }
    while(fast && slow){
        fast = fast.next
        slow = slow.next
    }
    slow.next = slow.next.next
    return ret.next
};

面试题 02.07. 链表相交

同:160.链表相交

力扣题目链接

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

image.png

image.png


解题思路

分别获取两个链表的差值,然后“对齐”两个链表的尾部,通过长度差移动较长的那个链表的指针,与较短的链表的头指针对齐,然后一起向后遍历,遇到相等的情况下返回节点。

var getIntersectionNode = function(headA, headB) {
    let nodeA = headA
    let lenA=lenB = 0
    let nodeB = headB
    let diff = 0
    while(nodeA || nodeB){
        if(nodeA){
            lenA ++
            nodeA = nodeA.next
        }
        if(nodeB){
            lenB ++
            nodeB = nodeB.next
        }
    }
    diff = Math.abs(lenA-lenB)
    nodeA = headA
    nodeB = headB
    if(lenB > lenA){
        while(diff--){
            nodeB = nodeB.next
        }
        while(lenA--){
            if(nodeB === nodeA){
                return nodeB
            }
            nodeB = nodeB.next
            nodeA = nodeA.next
        }
    }else{
        while(diff--){
            nodeA = nodeA.next
        }
        while(lenB--){
            if(nodeB === nodeA){
                return nodeB
            }
            nodeB = nodeB.next
            nodeA = nodeA.next
        }
    }
    return null
};

上述代码是我没看题解的情况下写的代码,基本的算法思路契合,但是代码有很大的冗余,不简洁。

可以在获取链表长度移动指针尾对齐链表的时候进行代码复用操作。

var getListLen = function(head) {
    let len = 0, cur = head;
    while(cur) {
       len++;
       cur = cur.next;
    }
    return len;
}
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;
};

142.环形链表II

力扣题目链接

题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

image.png

解题思路

这道题的解题思路可以分为两部分:

  • 判断链表中是否有环

    在判断链表中是否有环的时候,使用快慢指针方法,一开始将slow 与 fast 同时指向头结点, slow节点一次前进一个位置,fast 节点一次前进两个位置,这样的话,如果链表中有环,那么快慢指针一定会相遇。

    如果快指针最后指向null,说明遍历到链表的末尾,链表内不存在环,返回 null.

  • 判断环的入口

    在第一次的解题过程中,我只想到了判断链表是否有环的方法,没有进一步找到环的入口。

    实际上看完题解并写完代码后,这个后半部分的思路需要一些简单的数学运算和图解配合:

27f6a2b3421fc35ede5edb8d21f6712.jpg

此时,创建新指针ptr指向头,和slow指针一起按照一步每次的速度进行遍历,最后同步到达环的入口点。

当 ptr === slow, ptr 和 slow 指向的均是入口点,返回该节点即可。

var detectCycle = function(head) {
     if(head == null) return null

    let fast = head
    let slow = head

    while(fast){
        if(!fast.next) return null
        fast = fast.next.next
        slow = slow.next
        if(fast === slow){
            let ptr = head
            while(ptr != slow){
                slow = slow.next
                ptr = ptr.next
            }
            return ptr
        }
        
    }
    return null
};