代码随想录Day4 | 24. 两两交换链表中的节点 19.删除链表的倒数第N个节点 面试题 02.07. 链表相交 142.环形链表II

代码随想录Day | 24. 两两交换链表中的节点 19.删除链表的倒数第N个节点 面试题 02.07. 链表相交 142.环形链表II

  • 两两交换链表中的节点
  • 删除链表的倒数第N个节点
  • 链表相交
  • 环形链表II

两两交换链表中的节点

文档讲解:代码随想录
视频讲解: 帮你把链表细节学清楚! | LeetCode:24. 两两交换链表中的节点
状态:√

  1. 思路
    和昨天的翻转链表差不多,对于一组节点来说,我们要做的就是,
    1. 先保存第二个节点
    2. 将第一个节点指向第二个节点的后面那个节点
    3. 将第二个节点指向第一个节点
    4. 将头指针指向第二个节点
    5. 移动头指针,到下一组节点之前的那个节点上,也就是上一组反转之后的第二个节点,因为我们采用的虚拟头结点的方式
      代码如下
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* First = new ListNode(0,head);
        ListNode* cur = First;
        ListNode* temp;
        while(cur->next&&cur->next->next)
        {
            //记录第二个节点
            temp = cur->next->next;
            //将第一个节点的指向指向第二个节点的下一个节点
            cur->next->next = temp->next;
            //将第二个节点的指向指向第一个节点
            temp->next = cur->next;
            //将虚拟头结点的指向指向第二个节点
            cur->next = temp;
            //移动头结点到下一次调转列表之前
            cur = cur->next->next;
            temp = nullptr;
        }
        delete temp;
        return First->next;
    }
};

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

文档讲解:代码随想录
视频讲解: 链表遍历学清楚! | LeetCode:19.删除链表倒数第N个节点
状态:√

  1. 思路
    倒数第n个就是翻转链表后正数第n个,所以考虑先翻转链表,然后定义一个头结点,找到第n个之前的那个节点,将其指向改变就可以删除了。
    需要注意的是,下标从0开始,为了找到第n个节点,所以循环的判断应该是n-1。
    代码如下
	class Solution {
public:
    ListNode* reverseList(ListNode* head)
    {
        ListNode* cur = head;
        ListNode* res = nullptr;
        ListNode* temp;
        while(cur)
        {
            temp = cur->next;
            cur->next = res;
            res = cur;
            cur = temp;
        }
        delete temp;
        return res;
    }

public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* resList = reverseList(head);
        ListNode* First = new ListNode(0,resList);
        ListNode* cur = First;
        while(n-1)
        {
            cur = cur->next;
            n--;
        }
        if(cur->next)
        {
            cur->next = cur->next->next;
        }
        ListNode* resB = reverseList(First->next);
        return resB;
    }
};
  1. 双指针
    不用翻转链表,用双指针,由快指针先移动n+1步,然后两指针同时移动,当快指针指向的值为空时,慢指针刚好到达要删除的前一位。
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* First = new ListNode(0,head);
        ListNode* fast =First;
        ListNode* slow = fast;
        while(n+1)
        {
            fast = fast->next;
            n--;
        }
        while(fast)
        {
            slow = slow->next;
            fast = fast->next;
        }
        slow->next = slow->next->next;
        return First->next;
    }
};

链表相交

文档讲解:代码随想录
视频讲解:
状态:√

  1. 思路
    本题,主要就是两个链表的长度不一致,导致在移动的过程中,会错开找不到相交点,所以需要先将两个链表的长度变为一致,这里来的长度一致,是指从头结点到相交节点的长度一致。剩下的就是利用循环去寻找相等的点了。
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int a = 0;

        int lengthA = 0;
        int lengthB = 0;
        ListNode* calA = headA;
        ListNode* calB = headB;
        while(calA)
        {
            calA = calA->next;
            lengthA++;
        }
        while(calB)
        {
            calB = calB->next;
            lengthB++;
        }
        if(lengthA>lengthB)
        {
            while(lengthA-lengthB)
            {
                headA = headA->next;
                lengthA--;
            }
        }
        else
        {
            while(lengthB-lengthA)
            {
                headB = headB->next;
                lengthB--;
            }
        }

        while(headA&&headB)
        {
            if(headA == headB)
            {
                a = 1;;
                break;
            }
            else
            {
                headA =headA->next;
                headB =headB->next;
            }
        }
        if(a)
        {
            return headA;
        }
        else
        {
            return NULL;
        }
    }
};

文档里面利用了swap将长的链表全部变为A链表,可以省去一个循环。

if(lengthA < lengthB)
{
	swap(headA,headB);
	swap(lengthA,lengthB);
}

时间复杂度: O ( m + n ) O(m+n) O(m+n)

  1. 双指针
    详细解法参考力扣的题解
    有两链表在交点之前的长度分别为a,b,相交之后的长度为c,第一次扫描链表两链表扫描指针走过的长度分别为a+c和b+c。当两链表的扫描指针到达尾端的时候,将指针的指向分别改为指向对方链表的头节点。这样扫描指针再次到达相交节点的时候,两个扫描指针走过的长度为a+c+b和b+c+a。就可以求得了。

环形链表II

文档讲解: 代码随想录
视频讲解:把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表II
状态: 起点的求解原理

  1. 思路
    本题分为两点
    1. 是否有环:快慢指针,快指针一次走两格,慢指针一次走一格,如果两个指针能够相遇,那么说明有环。
    2. 求起点:我们假设环的起点和链表头节点之间距离是k,环的起点到相遇点是l,相遇点到环的起点是m。
      那么有等式,在相遇的时候成立–慢指针路径的两倍等于快指针走过的路径,n表示环内循环
      2 ∗ ( k + l ) = n ( l + m ) + l + k 2*(k+l) = n(l+m)+l+k 2(k+l)=n(l+m)+l+k
      通过整理可以得到
      k = m + ( n − 1 ) ( l + m ) k = m+(n-1)(l+m) k=m+(n1)(l+m)
      k表示头节点到起点的距离,m表示相遇点到起点的距离,说明如果我们从头节点和相遇点同时有指针移动,那么会在起点相遇。
  2. 为什么会寻找下面的等式:因为我们只直到头节点和相遇点的位置,所以要利用这两个位置
    最终代码如下
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        //快慢指针,快指针2倍速,慢指针正常,若相遇则有环
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast&&fast->next)
        {
            slow = slow->next;
            fast =  fast->next->next;
            if(slow == fast)
            {
                //求环的起点,从相遇点出发一个指针正常,起点出发一个指针正常
                ListNode* fastFind = slow;
                ListNode* slowFind = head;
                while(slowFind != fastFind)
                {
                    slowFind = slowFind->next;
                    fastFind = fastFind->next;
                }
                return slowFind;
                break;
            }
        }
        return NULL;

    }
};

你可能感兴趣的:(数据结构)