Day4 | 24. 两两交换链表中的节点 19.删除链表的倒数第N个节点 160.链表相交 142.环形链表II

27 阅读5分钟

24. 两两交换链表中的节点 - 力扣(LeetCode)

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 image.png

image.png 定义一个cur指针,处理cur指向的节点之后的2个节点,完成互换。

while的判断是cur->next&&cur->next->next,也就是cur之后同时有两个节点,才需要互换,如果之后只有一个节点,没有其余节点跟它互换;如果之后没有节点,自然不用再互换。

在②指向①之前,需要保存③,否则链表无法前进,因此用p1p2p3分别记录三个节点的地址

dummynext应该是p2

p2next应该是p1

p1next应该是p3

之后,应该让cur移动到p1,处理后续的③和④

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummy = new ListNode();  //虚拟头节点
        dummy->next = head;

        ListNode* cur = dummy;
        ListNode* p1 = dummy;
        ListNode* p2 = dummy;
        ListNode* p3 = dummy;
        while(cur->next&&cur->next->next){ //后两个节点非空
            //记录
            p1 = cur->next;
            p2 = p1->next;
            p3 = p2->next;
            //交换
            cur->next = p2;
            p2->next = p1;
            p1->next = p3;
            //前进
            cur = p1;
        }

        return dummy->next;
    }
};

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

image.png

fast停在尾节点

fast先走n步,根据循环条件while(fast->next),尾节点无法进入循环,因此循环结束,fast停留在尾节点。

此时slown步走到尾节点,也就是第倒数n+1个节点,此时slow->next = slow->next->next可以删除倒数第n个节点。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode();
        dummy->next = head;
        ListNode* fast = dummy;
        ListNode* slow = dummy;
        while(n--){
            fast = fast->next;//fast相比slow提前走n步
        }
        while(fast->next){ //停在尾节点
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next;
        return dummy->next;
    }
};

fast停在nullptr

n步到nullptr,就是倒数第几个节点。

fast停在nullptr,希望slow相距n+1步赶上fast,因此让fastn+1步。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode();
        dummy->next = head;
        ListNode* fast = dummy;
        ListNode* slow = dummy;
        while(n--){
            fast = fast->next;//fast相比slow提前走n步
        }
        fast = fast->next;//再走一步,就是n+1步
        while(fast){ //停在nullptr
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next;
        return dummy->next;
    }
};

160. 相交链表 - 力扣(LeetCode)

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:

image.png 题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

🌟链表相交,不是值相同,而是节点地址相同!

由于链表长度不一致,从头开始遍历时,即使相交,也可能不会同时访问同一节点。

末尾对齐

由于链表一旦相交,不可能再分裂,因此可以末尾对齐,再齐头并进访问 image.png---(图片来自代码随想录)

\\待补充...

拼接获得等长链表

拼接两个链表,从头遍历,可以同时进入相交部分,原因如下:

假设相交之前,链表B相比A长了n节点,因此会晚n步开始从头遍历A,而A相比B短了n节,因此同时第二次进入相交部分。

image.png---(图片来自双指针技巧秒杀七道链表题目 | labuladong 的算法笔记)

如图所示,遍历两个拼接链表的指针会同时访问相交的第一个节点,返回此节点即可

  • 若链表不相交,会不会死循环?

  • 不会,由于拼接链表等长,会同时访问空指针,因此会返回nullptr并退出循环

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

142. 环形链表 II - 力扣(LeetCode)

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。

注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。

示例

输入: head = [3,2,0,-4], pos = 1

输出: 返回索引为 1 的链表节点

解释: 链表中有一个环,其尾部连接到第二个节点。

后续补上分析...

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast&&fast->next){
            fast = fast->next->next;
            slow = slow->next;
            if(slow == fast){ //fast套圈slow
                fast = head;  //fast回到头节点
                while(slow != fast){
                    fast = fast->next;  //单步前进
                    slow = slow->next;
                }
                return slow;
            }
        }
        return nullptr;
    }
};

思考

while(fast->next && fast->next->next){
    fast->next->next;
    //fast->next 非空 则fast->next->next不会出现空指针异常(nullptr->next)
    //说明fast连走两步不会出现异常

    //同上,fast->next->next非空,则可以连走三步

    //最后一次满足条件是倒数第三个节点,进入while后,fast移动到尾节点,并结束while循环

    //循环判断没有fast,若fast一开始就为空,fast->next出现异常
}
while(fast && fast->next){
    fast->next->next;
    //fast非空 则fast->next不会出现空指针异常(nullptr->next)
    //说明fast走一步不会出现异常

    //同上,fast->next非空,则可以连走两步

    //最后一次满足条件是倒数第二个节点,进入while后,fast移动到nullptr,并结束while循环

}