代码随想录算法训练营第四天|LeetCode24.两两交换链表中的结点、LeetCode19.删除链表的倒数第N个结点、LeetCode面试题02.07. 链表相交、LeetCode142.环形链表Ⅱ

目录

前言

LeetCode24.两两交换链表中的结点

LeetCode19.删除链表的倒数第N个结点

LeetCode面试题02.07. 链表相交

LeetCode142.环形链表Ⅱ

一、LeetCode24 两两交换链表中的结点

题目链接:两两交换链表中的结点

设置虚拟头结点代码:

代码思路:

结点指针修改流程:

 二、LeetCode19 删除链表的倒数第N个结点

题目链接:删除链表的倒数第N个结点

双指针法:

代码思路:

三、LeetCode面试题 链表相交

题目链接:链表相交

双指针法:

代码思路:

四、LeetCode142 环形链表Ⅱ

题目链接:环形链表Ⅱ

双指针法代码:

代码思路:


前言

LeetCode24.两两交换链表中的结点

文章链接

视频链接

LeetCode19.删除链表的倒数第N个结点

文章链接

视频链接

LeetCode面试题02.07. 链表相交

文章链接

LeetCode142.环形链表Ⅱ

文章链接

视频链接


一、LeetCode24 两两交换链表中的结点

题目链接:两两交换链表中的结点

设置虚拟头结点代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode *newHead = new ListNode(0, head);
        ListNode *pFir = nullptr;
        ListNode *pPre = newHead;
        while(pPre->next && pPre->next->next)
        {
            pFir = pPre->next;
            pPre->next = pPre->next->next;
            pFir->next = pFir->next->next;
            pPre->next->next = pFir;
            pPre = pPre->next->next;
        }
        return newHead->next;
    }
};

代码思路:

设置一个虚拟头结点newHead, 然后根据下图指针修改流程来循环修改指针

结点指针修改流程:

 二、LeetCode19 删除链表的倒数第N个结点

题目链接:删除链表的倒数第N个结点

双指针法:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode *newHead = new ListNode(0, head);
        ListNode *fast = newHead;
        ListNode *slow = newHead;
        while(n-- && fast->next)
        {
            fast = fast->next;
        }
        if(n >= 0)
            return nullptr;
        
        while(fast->next)
        {
            slow = slow->next;
            fast = fast->next;
        }
        ListNode *delNode = slow->next;
        slow->next = delNode->next;
        delete delNode;
        return newHead->next;
    }
};

代码思路:

设置快慢两个指针,如果要找倒数第n个结点,首先让快指针从newHead指针处先向后移动n个结点,然后slow和fast同时移动,直到fast的下一个结点为NULL时,此时slow的下一个结点就是我们要找的倒数第n个结点。假设我们要找倒数第3个结点,详细的流程如下图:

首先移动fast指针:

 再同时移动fast和slow指针:

 最后就是通过slow指针进行正常的删除操作即可。

三、LeetCode面试题 链表相交

题目链接:链表相交

双指针法:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *curA = headA;
        ListNode *curB = headB;
        int lenA = 0;
        int lenB = 0;
        while(curA)
        {
            curA = curA->next;
            lenA++;
        }
        while(curB)
        {
            curB = curB->next;
            lenB++;
        }

        curA = headA;
        curB = headB;
        // 让curA指向比较长的链表
        if(lenA < lenB)
        {
            swap(curA, curB);
            swap(lenA, lenB);
        }

        for(int i = 0; i < lenA - lenB; i++)
        {
            curA = curA->next;
        }

        while(curA)
        {
            if(curA == curB)
            {
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }

        return nullptr;

    }
};

代码思路:

如果链表A和链表B存在相交,那么A和B链表的倒数几个结点必然是一样的。所以先得到A和B的链表长度lenA和lenB(此时假设lenA>lenB)。然后让链表A的结点向后移动lenA - lenB个结点,最后开始同时遍历,直到找到相同的结点或者遍历到链表末尾为止。

四、LeetCode142 环形链表Ⅱ

题目链接:环形链表Ⅱ

双指针法代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *fast = head;
        ListNode *slow = head;
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast)
            {
                slow = head;
                while(fast != slow)
                {
                    fast = fast->next;
                    slow = slow->next;
                }
                return fast;
            }
        }
        return nullptr;
    }
};

代码思路:

如下图,假设链表有环,链表的直线部分长度为x,从环的入口处到fast和slow指针相交处长度为y,从相交处回到环的入口处的长度为z。

可得快指针fast走过的长度为:L_{fast} = x + y + n(y + z)(其中n代表走过的环形链表的圈数)

慢指针slow的走过的长度:L_{slow} = x + y

联立两个式子,由于L_{fast}L_{slow}的两倍,所以有2 * L_{slow} = L_{fast}

可得:2(x + y) = x + y + n(y + z)

再化简进行加减消元:x = n(y + z) - y

该式子还可以转换为:x = (n - 1)(y + z) + z这个式子就是最重要的关系式

通过最后的关系式,我们可以得知,从fast和slow相遇的位置开始移动n - 1圈再加上距离z,刚刚好就等于x的距离,所以此时,我们只要再让一个新的指针从head出发,当前指针从相交点出发,不断往前移动一格,最终一定会再环的入口处相交!


 总结:

在涉及到单链表的删除或者查找的时候,可以优先想一想是否能采用双指针的办法来锁定到要求的结点,通过双指针法可以减少遍历的次数,得到更小的时间复杂度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值