【LeetCode刷题week2】——链表专题

LC 链表专题

[声明]:ACWing Y总课程 总结

1.题目链接

2009年408真题——408元年的数据结构算法题!!!

LeetCode19. 删除链表的倒数第 N 个结点

输入 / 输出样例

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
------------------------
输入:head = [1], n = 1
输出:[]

在这里插入图片描述
代码

/**
 * 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) {
        auto dummy = new ListNode(-1);
        dummy->next = head; // 创建虚拟头结点, 并指向head
        // 虚拟头结点目的是为了防止头结点被删掉
        auto first = dummy, second = dummy;\
        while(n --) first = first->next;
        while(first->next){ // first 为 nullptr 退出
            first = first->next;
            second = second->next;
        }
        second->next = second->next->next; // delete

        return dummy->next; // 虚拟头结点的下一个结点一定是头结点, 且预防了头结点被删除
    }
};

2.题目链接

LeetCode237. 删除链表中的节点

输入 / 输出样例

输入:head = [4,5,1,9], node = 5
输出:[4,1,9]

思路:伪装成下一个节点,把下一个结点删除即可

代码如下:

  • 版本一
class Solution {
public:
    void deleteNode(ListNode* node) { // node是给定结点
        node->val = node->next->val;
        node->next = node->next->next;
    }
};
  • 版本二
class Solution {
public:
    void deleteNode(ListNode* node) { // node是给定结点
         *(node) = *(node->next);  // 将结构体整段赋值
    }
};

3.题目链接

LeetCode83. 删除排序链表中的重复元素

输入 / 输出样例

输入:head = [1,1,2,3,3]
输出:[1,2,3]

代码如下:
1.方法一:

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        auto dummy = new ListNode(-1);
        dummy->next = head;
        auto p = dummy;
        while(p->next){
            if(p->next->val == p->val){
                p->next = p->next->next;
                continue;
            }
            else{
                p = p->next;
            }
        }
        return head;
    }
};

2.y总代码

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        auto p = head;
        while(p){
            if(p->next && p->next->val == p->val)
                p->next = p->next->next;
            else p = p->next;
        }
        return head;
    }
};

4.题目链接

LeetCode61. 旋转链表

在这里插入图片描述

输入 / 输出样例

输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]

代码如下:

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(!head) return nullptr;
        int n = 0;
        for(auto p = head; p; p = p->next) n++;
        k %= n;
        auto first = head, second = head;
        while(k--) first = first->next;
        while(first->next){
            first = first->next;
            second = second->next;
        }

        first->next = head;
        head = second->next;
        second->next = nullptr;

        return head;
    }
};

5.题目链接

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

在这里插入图片描述

输入 / 输出样例

输入:head = [1,2,3,4]
输出:[2,1,4,3]

代码如下:

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(!head) return nullptr;
        auto dummy = new ListNode(-1);
        dummy->next = head;

        auto p = dummy;
        while(p->next && p->next->next){
            auto a = p->next;
            auto b = a->next;
            // 交换相邻结点
            p->next = b;
            a->next = b->next;
            b->next = a;
            p = a;
        }
        return dummy->next;
    }
};

6.题目链接

LeetCode206. 反转链表

在这里插入图片描述

输入 / 输出样例

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

代码如下:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head) return nullptr;
        auto a = head;
        auto b = a->next;
        while(b){
            auto c = b->next;
            b->next = a;
            a = b;
            b = c;
        }
        head->next = nullptr; // 头结点变为尾结点
        return a; // a为新的头结点
    }
};

7.题目链接

LeetCode92. 反转链表 II

在这里插入图片描述

输入 / 输出样例

输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]

代码如下

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        if(m == n) return head;
        auto dummy = new ListNode(-1);
        dummy->next = head;

        auto a = dummy, d = dummy;
        for(int i = 0; i < m-1; i++) a = a->next; // a为m-1位置的结点
        for(int i = 0; i < n; i++) d = d->next;   // d为n位置的结点

        auto b = a->next, c = d->next;
        // [模板]反转链表
        auto p = b, q = b->next; // p q r为模板的a b c
        while(q != c){ // 反转区间[b,d]
            auto r = q->next;
            q->next = p;
            p = q;
            q = r;
        }  

        b->next = c;
        a->next = d;
        return dummy->next;
    }
};

8.题目链接

LeetCode160. 相交链表

示例
在这里插入图片描述
思路:

  • p从ListA开始遍历,q从ListB开始遍历,都遍历完后。换链表遍历,一定会同时相交
    在这里插入图片描述代码如下
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        auto p = headA, q = headB;
        while(p != q){ // p q相遇时退出
            if(p) p = p->next;
            else p = headB; // 若p空, 则继续遍历B
            if(q) q = q->next;
            else q = headA; // 若q空, 则继续遍历A
        }

        return p;
    }
};

9.题目链接

LeetCode142. 环形链表 II

示例
在这里插入图片描述

在这里插入图片描述
自己分析(破烂证明):

∵ fast和slow一定会相遇,假设在c点相遇,b~c = y,C为环周长,n为圈数
fast距离: x + nC + y
slow距离: x+y
∵ x + nC + y = 2x + 2y
∴ x = nC -y
∴ x+y=nC
不妨设n = 1
∴ x+y = C
∴ c~b = x
即 c~b = a~b = x
∴ 将一个指针移到head,两个指针同时移动x距离后,一定在环的入口相遇。
得证!

代码

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

10.题目链接 ★★

LeetCode148. 排序链表

略难

在这里插入图片描述
在这里插入图片描述

代码如下

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        int n = 0;
        for(auto p = head; p; p = p->next) n++;
        auto dummy = new ListNode(-1);
        dummy->next = head;
        for(int i = 1; i < n; i *= 2){ // 归并长度
            auto cur = dummy;
            for(int j = 0; j + i < n; j += i * 2){ // 每次扫描长度为 2*i 的归并段
                // left为左归并段端点, right为右归并段端点
                auto left = cur->next, right = cur->next;
                for(int k = 0; k < i; k++) right = right->next; // right为第二个归并段的左端点
                int l = 0, r = 0;  // l r记录当前左/右归并段是否遍历完
                // ★归并两段长度为 i 的区间
                while(l < i && r < i && left && right){
                    if(left->val <= right->val){ // 归并排序 l_a <= r_b
                        cur->next = left;
                        cur = left;
                        left = left->next;
                        l ++;
                    }else{ // 归并排序 l_a > r_b
                        cur->next = right;
                        cur = right;
                        right = right->next;
                        r ++;
                    }
                }
                // 归并最后剩余的部分
                while(l < i && left){
                    cur->next = left;
                    cur = left;
                    left = left->next;
                    l ++;
                }
                while(r < i && right){
                    cur->next = right;
                    cur = right;
                    right = right->next;
                    r ++;
                }
                cur->next = right;
            } // for j
        } // for i
        return dummy->next;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值