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

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

题目链接:24. 两两交换链表中的节点

文档讲解:代码随想录

视频讲解:帮你把链表细节学清楚! | LeetCode:24. 两两交换链表中的节点

思路

首先为便于处理头结点,设置一个虚拟头结点dummyHead。设置一个cur指针指向需要交换结点的前驱结点,当cur->nextcur->next->next都存在时说明后续两个结点需要交换,此时利用临时结点变量记录好结点后,完成交换操作。
交换结点操作

代码

class Solution
{
public:
    ListNode *swapPairs(ListNode *head)
    {
        ListNode *dummyHead = new ListNode(0, head);
        ListNode* cur = dummyHead;
        while (cur->next && cur->next->next)
        {
            ListNode* temp1 = cur->next;
            ListNode* temp2 = cur->next->next->next;
            cur->next = temp1->next;
            cur->next->next = temp1;
            temp1->next = temp2;
            cur = cur->next->next;
        }
        return dummyHead->next;
    }
};

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

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

文档讲解:代码随想录

视频讲解:链表遍历学清楚! | LeetCode:19.删除链表倒数第N个节点

思路

删除链表的结点时,需要知道所删除结点的位置,若知道所删除结点据头结点head的位置,则一次遍历至所删除结点的前驱结点,然后完成删除操作;若直到所删除结点据尾结点的位置N,则可以通过两个相距N的指针,将后指针指向尾结点,则前指针指向所删除结点的前驱结点,然后完成删除操作。

代码

class Solution
{
public:
    ListNode *removeNthFromEnd(ListNode *head, int n)
    {
        ListNode *dummyHead = new ListNode(0, head);
        ListNode *pre = dummyHead;
        ListNode *cur = dummyHead->next;
        ListNode *beh = cur;
        n--;
        while (n--)
        {
            beh = beh->next;
        }
        while (beh->next)
        {
            pre = pre->next;
            cur = cur->next;
            beh = beh->next;
        }
        ListNode *temp = cur;
        pre->next = cur->next;
        delete temp;
        return dummyHead->next;
    }
};

相关题目

26. 删除有序数组中的重复项
283. 移动零
844. 比较含退格的字符串
977. 有序数组的平方

面试题 02.07. 链表相交

题目链接:面试题 02.07. 链表相交

文档讲解:代码随想录

思路

两个链表相交指的是两个链表的结点地址相同,而不是值相同,如下图中实际相交结点为8,而不是1。

链表相交的思路是设置两个指针分别指向两个链表相交结点前的相同位置,如下图中的4和0,这样让两个指针分别向后走,即可找到相交结点。

所以首先要求出链表B的0位置,链表B和链表A的长度差就是链表B的0相对于head结点的位置,因此可以先分别遍历两个链表,求出两个链表的长度差l,然后设置一个结点指向链表B的头结点,向后走l个,这样链表A和链表B的结点就对齐了。

链表相交

class Solution
{
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
    {
        int len1 = 0;
        int len2 = 0;
        ListNode *dummyHead1 = new ListNode(0, headA);
        ListNode *dummyHead2 = new ListNode(0, headB);
        ListNode *p = dummyHead1;
        while (p->next)
        {
            p = p->next;
            len1++;
        }
        p = dummyHead2;
        while (p->next)
        {
            p = p->next;
            len2++;
        }
        if (len1 < len2)
            return getIntersectionNode(headB, headA);
        int gap = len1 - len2;
        p = headA;
        ListNode *q = headB;
        while (gap--)
            p = p->next;
        while (p && q && p != q)
        {
            p = p->next;
            q = q->next;
        }
        if (p && q && p == q)
            return p;
        else
            return NULL;
    }
};

142. 环形链表II

题目链接:142. 环形链表 II

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

思路

首先要判断该链表是否有环,然后再找到环的入口。

使用快慢双指针法,慢指针一次走一个结点,快指针一次走两个结点,如果快慢指针相遇(即指向同一结点),则该链表有环,否则链表没有环。

当链表有环时,找到该环的入口是这道题的难点,需要推导一下。

推到环入口
如上图,假设链表的头结点至环入口的结点数为 x x x,环入口至快慢指针相遇点的结点数为 y y y,快慢指针相遇点至环入口的结点数为 z z z

则快慢指针相遇时,慢指针走了 x + y x+y x+y个结点,快指针走了 x + y + n ( y + z ) x+y+n(y+z) x+y+n(y+z)个结点,其中 n n n代表圈数。

因为快指针的速度是满指针的两倍,故有 x + y + n ( y + z ) = 2 ( x + y ) x+y+n(y+z) = 2(x+y) x+y+n(y+z)=2(x+y)

整理得到 x = z + ( n − 1 ) ( y + z ) x=z+(n-1)(y+z) x=z+(n1)(y+z)

上式表示,环入口至头结点的距离 x x x等于 ( n − 1 ) (n-1) (n1)圈环加快慢指针相遇点至环入口的结点数。

因此设置一个指针1指向头结点,一个指针2指向快慢指针相遇点,两个指针同步向后走,指针2在环中绕 n − 1 n-1 n1圈后再走 z z z个结点就与指针1在环入口相遇。

代码

class Solution
{
public:
    ListNode *detectCycle(ListNode *head)
    {
        if (!head || !head->next)
            return NULL;
        ListNode *slow = head;
        ListNode *fast = head;
        ListNode *start = head;
        while (fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
            if (slow == fast)
            break;
        }
        if (fast == slow)
        {

            while (fast != start)
            {
                start = start->next;
                fast = fast->next;
            }
            return start;
        }
        else
        {
            return NULL;
        }
    }
};

总结

算法训练营中关于链表的问题今天就结束了。链表问题最常用的方法是双指针法,必须要明确两个指针的含义,同时要处理好指针的边界。

  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值