LeetCode刷题笔记【4】:链表专题-2(两两交换链表中的节点,删除链表的倒数第N个节点,链表相交,环形链表)

前置知识

参考前文:

LeetCode刷题笔记【3】:链表专题-1(移除链表元素,设计链表,反转链表)

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

题目描述

截图

LeetCode链接:https://leetcode.cn/problems/swap-nodes-in-pairs/description/

解题思路

没有太多注意,按照题目所说的进行交换即可
可能对于pre、cur、next的使用有点技巧

代码

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(head==NULL)
            return head;
        ListNode* dummyHead = new ListNode(0, head);
        ListNode* cur=head;
        ListNode* prev=dummyHead;
        while(1){
            ListNode* next = cur->next;
            if(cur==NULL || cur->next==NULL)
                break;
            prev->next = next;
            cur->next = next->next;
            next->next = cur;

            prev = cur;
            cur = cur->next;

            if(cur==NULL || cur->next==NULL)
                break;
        }
        return dummyHead->next;
    }
};

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

题目描述

截图

LeetCode链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/

简单快慢指针

思路: 快慢指针, 快指针先遍历一遍得到链表总体长度length, 慢指针在得知链表长度后再出发
就删除length-n的节点即可

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if(head==NULL)
            return head;

        ListNode* dummyHead = new ListNode(0, head);
        ListNode* fast = dummyHead;
        ListNode* slow = dummyHead;
        int length=0;
        while(fast->next != NULL){
            fast = fast->next;
            length++;
        }
        length = length - n;
        for(int i=0; i<length; ++i){
            slow = slow->next;
        }
        ListNode* tmp = slow->next;
        slow->next = tmp->next;
        delete tmp;
        return dummyHead->next;
    }
};

进阶快慢指针

思路: 依然是快慢指针, 但是快指针先走, 走n步以后慢指针出发
快指针到达结尾的时候, 慢指针正好抵达目标
主打一个简洁

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if(head==NULL)
            return head;

        ListNode* dummyHead = new ListNode(0, head);
        ListNode* fast = dummyHead;
        ListNode* slow = dummyHead;
        
        for(int i=0; i<n; ++i){
            fast = fast->next;
        }
        while(fast->next != NULL){
            fast = fast->next;
            slow = slow->next;
        }

        ListNode* tmp = slow->next;
        slow->next = tmp->next;
        delete tmp;
        return dummyHead->next;
    }
};

面试题 02.07. 链表相交

题目描述

截图
在这里插入图片描述

LeetCode链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/description/

哈希表法

思路: 维护一个unordered_set, 遍历A的时候依次入, 然后遍历B, 有节点已存在那就返回这个节点
如果一直遍历完B都没有, 就返回NULL

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        unordered_set<ListNode*> set;
        while(headA!=NULL){
            set.insert(headA);
            headA = headA->next;
        }
        while(headB!=NULL){
            if(set.count(headB))
                return headB;
            headB = headB->next;
        }
        return NULL;
    }
};

双指针对齐法

用哈希表不是不行, 但是空间复杂度过高, 参考题解, 使用双指针法
观察发现: A B的后半段是一样的, 那么只要让二者向右对齐(两个指针距离各自的尾节点距离一致)
然后再同时往后移动, 如果二者有相交, 就可以相遇

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA=headA;
        ListNode* curB=headB;
        int lengthA=0, lengthB=0;
        while(curA != NULL){
            curA = curA->next;
            lengthA++;
        }
        while(curB != NULL){
            curB = curB->next;
            lengthB++;
        }
        // 让curA指向更长的
        if(lengthA > lengthB){
            curA = headA;
            curB = headB;
        }else{
            curA = headB;
            curB = headA;
        }
        int length = abs(lengthA-lengthB);
        for(int i=0; i<length; ++i){
            curA = curA->next;
        }
        while(curA != NULL){
            if(curA == curB)
                return curA;
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};

142.环形链表II

题目描述

截图

LeetCode链接:xxx(记得加点击跳转链接)

unordered_set法

建立一个unordered_set, 遍历链表的过程中依次insert, 发现重复的就返回

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        unordered_set<ListNode*> set;
        while(head!=NULL){
            if(set.count(head))
                return head;
            set.insert(head);
            head = head->next;
        }
        return NULL;
    }
};

快慢指针法

如何用快慢指针判断有无环

只要快指针一次走两步, 慢指针一次走一步, 只要有环, 就一定会在某个点相遇;
所以可以用快慢指针判断有无环.

如何用快慢指针找到环入口

在这里插入图片描述
如上图所示:
快指针走过的距离是: fast = x+n(z+y)+y
慢指针走过的距离是: slow= x+y
又由快慢指针的速度: fast = 2*slow
有: x+n(z+y)+y = 2*(x+y)
化简: n*z + n*y = x + y
即: x = z + (n-1)*(x+y)
所以如果在快慢指针相遇的时候, 有一个新指针(速度和慢指针一样)从头结点出发, 那么当新指针和慢指针相遇的时候, 二者就指向入口节点.
在这里插入图片描述

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

总结

发现没有? 就是掌握了链表的底层数据结构, 以及基础操作, 这些"上层"的题目真就只需要一些trick.
最容易出现bug的地方也不是思路不对, 而是基本的操作没办法下意识地正确实现.
基础不牢地动山摇啊

本文参考:







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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值