代码随想录算法训练营第四天 | 24.两两交换链表中的节点19.删除链表的倒数第N个节点160.相交链表142.环形链表II

Posted 蔚尺丈八声

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代码随想录算法训练营第四天 | 24.两两交换链表中的节点19.删除链表的倒数第N个节点160.相交链表142.环形链表II相关的知识,希望对你有一定的参考价值。

两两交换链表中的节点

【力扣】24.两两交换链表中的节点

// class ListNode 
//     int val;
//     ListNode next;

//     ListNode() 
//     

//     ListNode(int val) 
//         this.val = val;
//     

//     ListNode(int val, ListNode next) 
//         this.val = val;
//         this.next = next;
//     
// 

class Solution 
    public ListNode swapPairs(ListNode head) 
        /*
         * 思路:模拟法
         * 先加上虚拟结点,然后每三个步骤交换指针
         * 
         * 三个步骤相当于两两交换结点
         * 
         * 参考:
         * https://programmercarl.com/0024.%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE
         * %E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9.html#_24-%E4%B8%A4%E4%B8%A4%E4%
         * BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9
         * 
         */

        // 加上虚拟头结点
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;

        // 初始化三个指针,分别指向三个步骤中的第一、二、三、四个结点
        // cur永远指向第一个结点
        // 因为移动三次指针,所以要考虑四个结点(实际只交换firstNode和secondNode)
        ListNode cur = dummyNode;
        ListNode firstNode;
        ListNode secondNode;
        ListNode thirdNode; // 相当于剩下还没做交换的链表的头结点

        // 循环三个步骤,最终得到交换结点的链表
        // 循环条件不加cur.next.next.next != null因为只要交换cur后两个结点,确保后两个在就行
        while (cur.next != null && cur.next.next != null) 
            firstNode = cur.next;
            secondNode = cur.next.next;
            thirdNode = cur.next.next.next;

            // 三个步骤(交换fistNode和secondNode)
            // 交换后,firstNode在thirdNode前面,让cur指向firstNode,下次循环交换cur.next和cur.next.next
            cur.next = secondNode;
            secondNode.next = firstNode;
            firstNode.next = thirdNode;

            // cur移动,准备下一轮交换(cur相当于头结点,是cur后面两个结点交换)
            cur = firstNode;
        

        return dummyNode.next;
    

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

【力扣】19.删除链表的倒数第N个节点

// class ListNode 
//     int val;
//     ListNode next;

//     ListNode() 
//     

//     ListNode(int val) 
//         this.val = val;
//     

//     ListNode(int val, ListNode next) 
//         this.val = val;
//         this.next = next;
//     
// 

class Solution 
    public ListNode removeNthFromEnd(ListNode head, int n) 
        /* 
         * 思路:快慢双指针
         * 
         * 参考:
         * https://programmercarl.com/0019.%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E7%9A%84%E5%80%92%E6%95%B0%E7%AC%ACN%E4%B8%AA%E8%8A%82%E7%82%B9.html#%E6%80%9D%E8%B7%AF
         * 
         * 1、快指针先从虚拟头结点处先走n+1步。(n是链表倒数第n个结点)
         * 2、然后快指针和慢指针同时一块走,当快指针走到最后一个结点时,慢指针就停在了倒数第n+1个结点处。
         * 3、因为要删除一个结点,必须知道它的前一个结点,所以慢指针停在倒数第n+1个结点而不是倒数第n个结点处。
         * 
         */

        // 设置虚拟头结点
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;

        // 快慢双指针
        ListNode fastNode = dummyNode;
        ListNode slowNode = dummyNode;

        // 快指针优先移动到第n+1个结点处(虚拟头结点不算)
        for (int i = 0; i < n; i++) 
            fastNode = fastNode.next;
        

        // 然后慢指针和快指针同时移动
        // 当快指针停到链表最后一个结点时,慢指针停在倒数第n+1个结点处
        while (fastNode.next != null) 
            slowNode = slowNode.next;
            fastNode = fastNode.next;
        

        // 删除倒数第n个结点
        slowNode.next=slowNode.next.next;

        return dummyNode.next;
    

相交链表

【力扣】160.相交链表

// class ListNode 
//     int val;
//     ListNode next;

//     ListNode(int x) 
//         val = x;
//         next = null;
//     
// 

public class Solution 
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) 
        /*
         * 思路:双指针
         * 
         * 参考:
         * https://programmercarl.com/%E9%9D%A2%E8%AF%95%E9%A2%9802.07.%E9%93%BE%E8%A1%
         * A8%E7%9B%B8%E4%BA%A4.html#%E6%80%9D%E8%B7%AF
         * 
         * 本题求两个链表交点节点的指针。 这里要注意,交点不是数值相等,而是指针相等。
         * 1、curA指向链表A的头结点,curB指向链表B的头结点。
         * 2、求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB末尾对齐的位置。
         * 3、就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
         * 4、否则返回null。
         * 
         */

        ListNode curA = headA;
        ListNode curB = headB;

        // 获得两链表的长度
        int lenA = 0; // 链表A的长度
        int lenB = 0; // 链表B的长度
        // 遍历链表的循环条件,是node!=null还是node.next!=null
        // 取决于你想让node指针最终停留在链表哪个位置
        // 如果是停留在最后一个结点处,那么node.next!=null(一定要注意确保当前结点不为空)
        // 如果是停留在链表结尾处的null,那么node!=null
        while (curA != null) 
            lenA++;
            curA = curA.next;
        
        while (curB != null) 
            lenB++;
            curB = curB.next;
        
        // 设置链表较长的那个为链表A,另一个为链表B,这是为了统一后面的操作
        if (lenA < lenB) 
            // 交换长度
            int lenTmp = lenA;
            lenA = lenB;
            lenB = lenTmp;

            // 交换链表
            ListNode tmpNode = headA;
            headA = headB;
            headB = tmpNode;
        

        // curA,curB重新指向两链表第一个结点
        curA = headA;
        curB = headB;
        // 求出两链表长度差值,然后让curA从头移动到两链表末尾对齐时,curB的位置
        // (链表A上倒数第lenB个结点)
        int diff = lenA - lenB;
        while (diff > 0) 
            diff--;
            curA = curA.next;
        

        // curA,curB同时移动,直到两指针指向同一个结点处,如果没有则返回null
        while (curA != null) 
            if (curA == curB) 
                return curA;
            
            curA = curA.next;
            curB = curB.next;
        
        return null;
    

环形链表II

【力扣】142.环形链表II

// class ListNode 
// int val;
// ListNode next;

// ListNode(int x) 
// val = x;
// next = null;
// 
// 

public class Solution 
    public ListNode detectCycle(ListNode head) 
        /*
         * 思路:快慢指针
         * 
         * 设置一个快指针和一个慢指针,快指针的速度是慢指针的两倍。
         * 最开始快慢指针同时从head结点开始遍历,如果链表有环,那么最终快指针一定会在环中和
         * 慢指针相遇,此时,从相遇点处到环形入口点处的距离=从头结点到环形入口点处的距离。
         * 再设置两指针,一个指向相遇点,一个指向链表头结点,同时遍历就能到达环形入口点处。
         * 
         * 公式证明参考:https://programmercarl.com/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II
         * .html#%E6%80%9D%E8%B7%AF
         * 
         */

        ListNode slowNode = head;
        ListNode fastNode = head;
        while (fastNode != null && fastNode.next != null) 
            slowNode = slowNode.next;
            fastNode = fastNode.next.next;

            // 快慢指针相遇,找到相遇点
            if (slowNode == fastNode) 
                // 从相遇点处到环形入口点处的距离=从头结点到环形入口点处的距离
                ListNode p1 = fastNode;
                ListNode p2 = head;
                // 从head和相遇点,同时查找直至相遇,即环形入口点处
                while (p1 != p2) 
                    p1 = p1.next;
                    p2 = p2.next;
                
                return p1; // 环形入口点处
            

        
        return null;
    

以上是关于代码随想录算法训练营第四天 | 24.两两交换链表中的节点19.删除链表的倒数第N个节点160.相交链表142.环形链表II的主要内容,如果未能解决你的问题,请参考以下文章

代码随想录算法训练营第四天 | 24.两两交换链表中的节点19.删除链表的倒数第N个节点160.相交链表142.环形链表II

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

代码随想录算法训练营第三天 | 203.移除链表元素707.设计链表 206.反转链表

代码随想录算法训练营第三天 | 203.移除链表元素707.设计链表 206.反转链表

LeetCode与《代码随想录》链表篇:做题笔记与总结-JavaScript版

LeetCode与《代码随想录》双指针篇:做题笔记与总结-JavaScript版