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

第四天

以下题目皆在Carl老师的 代码随想录 中(页面左边有对应题目的目录)有详细讲解,这是博主学习的心得和日志,感兴趣的建议去阅读一下原文。

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

注意因为是两两交互,两两交换需要他们两前面一个节点来定位,考虑好步骤顺序,比喻成人的话,先保存好头再挪屁股,不然挪完屁股找不到头。

养成先保存链表的初始(下一个节点),再去改变(下一个节点)指向的习惯,避免节点信息丢失

/**
 * 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* dummyhead = new ListNode(0);
        dummyhead->next = head;
        ListNode* cur = dummyhead;
        while(cur->next != nullptr && cur->next->next != nullptr)
        {
            ListNode* tmp1 = cur->next;
            ListNode* tmp2 = cur->next->next->next;
            cur->next =cur->next->next;
            //恢复tmp1里的节点
            cur->next->next = tmp1;
            //恢复tmp2里的节点
            cur->next->next->next = tmp2;
            
            //向前移两位,准备下一轮交互,因为是两两交互,两两交换需要他们两前面一个节点来定位他们的位置
            cur = cur->next->next;


        }
        // 不能直接return head,因为旧的head已经交换了,所以只能用dummyhead->next来找新的head
        ListNode* result = dummyhead->next;
        delete dummyhead;
        return result;

    }
};

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

让快指针先走n+1步,和还在原地的慢指针构建一个长为n+1的“窗口”,随后快慢指针一起移动,直到停止,慢指针所在位置距离快指针n+1,即慢指针停在了倒数第n+1的节点上,即倒数第n节点的前一位节点,这也是为什么是n+1,这样才能做删除下一个节点(倒数第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* dummyhead = new ListNode(0);
        dummyhead->next = head;
        ListNode* fast = dummyhead;
        ListNode* slow = dummyhead;
        //先让快指针走n步,创建一个和还在起始点的慢指针形成长为n的窗口:符合倒数n个节点的目标
        while(n!=0 && fast!= nullptr)
        {
            fast = fast->next;
            n--;
        }  
        //快指针多走一步,形成n+1的位差窗口,这样让下面一起移动时,slow停在n节点的前一个节点才能做删除操作
            fast = fast->next;
        //快慢指针一起移动,移动这个n+1的窗口,直到fast到空
        while(fast!=nullptr)
        {
            fast = fast->next;
            slow = slow->next;
        }
        ListNode* tmp = slow->next;
        slow->next = slow->next->next;
        delete tmp;

        return dummyhead->next;
    }
};

Leetcode 面试题 02.07. 链表相交

先分别算长度,然后挪动长的链表指向头节点的指针到与段节点指针对齐,然后遍历判断两个链表的是否有相等的节点(内存地址),因为都是单链表,所以一旦相交,两个链表后面节点就是共用一个地址,因为链表是用头节点确认地址的。

时间复杂度 O(n+m)

/**
 * 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, lenB = 0;
            
            while(curA != nullptr){
                lenA++;
                curA = curA -> next;
            } 

            while(curB != nullptr){
                lenB++;
                curB = curB ->next;
            }
        
            curA = headA;
            curB = headB;
            int diff = 0;
            //对比两个链表的长度,将长的链表的指向头节点的指针往后移diff位,这样两个指针相当于在同一起跑线上
        if (lenA > lenB){
            diff = lenA - lenB;
            while(diff--){
                curA = curA->next;
            }

        } else{
            diff = lenB - lenA;
            while(diff--){
                curB = curB->next;
            }
        }

        //既然两个指针在同一起跑线,任意判断其中一个链表都可以
        while (curA != nullptr)
        {
            //这里判断是否都指向同一个节点,如果同一个节点(内存地址),那就是相交
            if (curA == curB){
                return curA;
            } else {
                //继续向后遍历
                curA = curA->next;
                curB = curB->next;
            }
        }
        //上面无返回交点,就是不相交,返回NULL
        return NULL;

    }
};

递归思路可以参考

可以这样理解,两个指针A, 和B,分别从两个链表头出发开始遍历,遍历完自己所在线路这里假设A就去另一条路B去遍历,因为我们本质要找的是两个是否有共用的一个节点内存地址,所以在这个同时遍历的过程中,如果两个链表有相交,两个指针总会遍历到同一个节点,即A, B 同时分别遍历(线路A+B) (线路B+A)的路线,遇到相同地址,返回节点。

时间复杂度 O(n+m)   相当于遍历了(A+B)路线上所有的节点

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *A = headA, *B = headB;
        while (A != B) {
            A = A != nullptr ? A->next : headB;
            B = B != nullptr ? B->next : headA;
        }
        return A;
    }
};

作者:Krahets
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/solutions/1190240/mian-shi-ti-0207-lian-biao-xiang-jiao-sh-b8hn/

Leetcode 142.环形链表II

/**
 * 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;
        //判断->next因为快指针一步走两个节点
        while(fast != nullptr && fast->next != nullptr){
            fast=fast->next->next;
            slow=slow->next;
            //快慢指针相遇之后,由推算得知,头节点到环入口的距离=相遇点往前距环入口剩余的距离
            if(fast == slow)
            {
                //构建一个从头节点出发的指针
                ListNode* cur=head;
                while(cur != slow)
                {
                    //因为slow是正常的遍历速度,所以直接用slow遍历,一起先前走,就一定会在环入口相遇
                    slow=slow->next;
                    cur=cur->next;
                }
                //返回环的入口
                return cur; 
            }

        }
        return NULL;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值