链表高频题

链表高频题

160. 相交链表

#include <vector>
#include <iostream>
#include <algorithm>

struct ListNode {
    int val;
    ListNode *next;

    ListNode(int x) : val(x), next(NULL) {}
};

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == nullptr || headB == nullptr) return nullptr;
        ListNode *a = headA;
        ListNode *b = headB;
        int diff = 0;
        while (a->next != nullptr) {
            a = a->next;
            diff++;
        }
        while (b->next != nullptr) {
            b = b->next;
            diff--;
        }
        // 根本就不重合
        if (a != b) return nullptr;
        // 较长的链表赋给 a
        if (diff > 0) {
            a = headA;
            b = headB;
        } else {
            a = headB;
            b = headA;
        }
        diff = abs(diff);
        // 较长的链表先走 diff 步
        while (diff-- != 0)
            a = a->next;
        // 距离尾节点距离相同时,同时出发
        while (a != b) {
            a = a->next;
            b = b->next;
        }
        return a;
    }
};

25. K 个一组翻转链表

#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

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 *reverseList(ListNode *head, ListNode *tail) {
        ListNode *pre = tail->next, *cur = head, *next;
        ListNode *nextNode = tail->next;
        while (cur != nullptr && cur != nextNode) {
            next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }

    ListNode *reverseKGroup(ListNode *head, int k) {
        // 虚拟头节点,接在链表最前面,使得第一段要反转的链表部分的处理和后面统一
        ListNode *dummyHead = new ListNode(0, head);
        ListNode *pre = dummyHead;
        ListNode *left = dummyHead;
        ListNode *right;
        // 后移次数
        int count;

        while (pre->next != nullptr) {
            // left、right 移到下一段要反转的链表的头部
            left = pre->next;
            right = left;
            // right 后移 k-1 个节点
            for (count = k - 1; count != 0 && right->next != nullptr; count--)
                right = right->next;
            // 链表元素总数小于 k
            if (count != 0) return dummyHead->next;
            // 把反转后的部分接到前面的链表上
            pre->next = reverseList(left, right);
            // 反转后 left 节点变成反转部分的尾节点,也就是下一段要反转的部分的上一个节点
            pre = left;
        }

        return dummyHead->next;
    }
};

138. 随机链表的复制

#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

class Node {
public:
    int val;
    Node *next;
    Node *random;

    Node(int _val) {
        val = _val;
        next = nullptr;
        random = nullptr;
    }
};

class Solution {
public:
    Node *copyRandomList(Node *head) {
        if (head == nullptr) return nullptr;
        Node *pre = head;

        // 遍历原链表,在每个节点后面插入新节点
        while (pre != nullptr) {
            // 复制原节点的值
            Node *node = new Node(pre->val);
            // 接在原节点的后面
            node->next = pre->next;
            pre->next = node;
            pre = pre->next->next;
        }

        pre = head;
        // 遍历链表,复制 random 指针
        while (pre != nullptr) {
            if (pre->random != nullptr)
                pre->next->random = pre->random->next;
            pre = pre->next->next;
        }

        pre = head;
        Node *res = head->next;
        Node *cur = head->next;
        // 分离出复制出的链表
        while (cur != nullptr && cur->next != nullptr) {
            // 改回原链表节点的 next 指针
            pre->next = pre->next->next;
            pre = pre->next;
            // 新链表的节点从原链表中分离出来,串在一起
            cur->next = cur->next->next;
            cur = cur->next;
        }
        // 原链表尾节点的 next 指针
        pre->next = nullptr;
        return res;
    }
};

234. 回文链表

#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

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:

    // 返回向上取整的中间节点
    // [1, 2, 3, 4] 返回 3
    ListNode *findMid(ListNode *head) {
        ListNode *slow = head;
        ListNode *fast = head;
        while (fast != nullptr && fast->next != nullptr) {
            fast = fast->next->next;
            slow = slow->next;
        }
        return slow;
    }

    // 原地反转
    ListNode *reverseList(ListNode *head) {
        ListNode *pre = nullptr;
        ListNode *cur = head;
        ListNode *next;
        while (cur != nullptr) {
            next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }

    bool isPalindrome(ListNode *head) {
        ListNode *mid = findMid(head);
        mid = reverseList(mid);

        ListNode *p = head;
        ListNode *q = mid;
        while (q != nullptr) {
            if (p->val != q->val) return false;
            p = p->next;
            q = q->next;
        }
        return true;
    }
};

142. 环形链表 II

#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

struct ListNode {
    int val;
    ListNode *next;

    ListNode(int x) : val(x), next(nullptr) {}
};

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        // 环外节点数 a,环内节点数 b
        // 快慢指针经过的节点个数关系: f = 2s
        // 相遇时: f = s + n*b -> s = n*b, f = 2*n*b
        // 走到入口节点经过的节点个数 k = a + n*b, 先前进 a 步到入口节点, 然后在环里转圈
        // f = 0, s = n*b -> f = a, s = a + n*b 相遇在入口节点

        ListNode *slow = head, *fast = head;
        while (fast != nullptr && fast->next != nullptr) {
            slow = slow->next;
            fast = fast->next->next;
            // 首次相遇时,slow 已经跑了 b 步,只需跑 a 步就能到达入口
            // fast 返回开头 head 节点,也只需跑 a 步就能到达入口
            // 此时 a 是几并不知道,但是可以确定的是,slow 和fast 都在跑 a 步就会在入口相遇
            if (slow == fast) {
                fast = head;
                // 此时 f = 0, s = 1*b
                while (slow != fast) {
                    slow = slow->next;
                    fast = fast->next;
                }
                // 结束时 f = a, s = a + 1*b
                return slow;
            }
        }

        return nullptr;
    }
};

148. 排序链表

#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

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 *merge(ListNode *l1, ListNode *l2) {
        if (l1 == nullptr || l2 == nullptr) return l1 == nullptr ? l2 : l1;
        ListNode *dummyHead = new ListNode();
        ListNode *pre = dummyHead;
        while (l1 != nullptr && l2 != nullptr) {
            if (l1->val < l2->val) {
                pre->next = l1;
                l1 = l1->next;
            } else {
                pre->next = l2;
                l2 = l2->next;
            }
            pre = pre->next;
        }
        if (l1 != nullptr) pre->next = l1;
        if (l2 != nullptr) pre->next = l2;
        return dummyHead->next;
    }

    // 时间复杂度 O(n * logn),额外空间复杂度 O(1),稳定
    ListNode *sortList(ListNode *head) {
        // 统计链表长
        int len = 0;
        ListNode *temp = head;
        while (temp != nullptr) {
            len++;
            temp = temp->next;
        }

        ListNode *dummyHead = new ListNode();
        dummyHead->next = head;

        // 步长每次乘二
        for (int gap = 1; gap < len; gap <<= 1) {
            ListNode *pre = dummyHead;
            ListNode *cur = dummyHead->next;

            // 每次从一组元素的首个元素节点开始(两个子链表为一组)
            while (cur != nullptr) {
                // 长度为 gap 的子链表 l1
                ListNode *l1 = cur;
                int i = 1;
                while (i < gap && cur->next != nullptr) {
                    cur = cur->next;
                    i++;
                }

                // 子链表 l2
                ListNode *l2 = cur->next;
                // 把 l2 从 l1 后面断开
                cur->next = nullptr;

                // 找到子链表 l2 的末尾,l2 可能是最后一个子链表并且长度小于等于 gap
                // l2 后面可能还有
                cur = l2;
                i = 1;
                while (i < gap && cur != nullptr && cur->next != nullptr) {
                    cur = cur->next;
                    i++;
                }

                ListNode *next = nullptr;
                // l2 后面还有节点时
                if (cur != nullptr) {
                    // 下一组的起点(两个子链表为一组)
                    next = cur->next;
                    // 断开,l2变成完成的一条链表
                    cur->next = nullptr;
                }

                // 把这组的两个子链表合并
                pre->next = merge(l1, l2);
                // pre 移到合并后的最后一个节点,等待接上下一组合并后的首个节点
                while (pre->next != nullptr)
                    pre = pre->next;
                // 进入下一组的归并
                cur = next;
            }
        }

        return dummyHead->next;
    }
};
posted @ 2024-09-28 23:30  n1ce2cv  阅读(4)  评论(0编辑  收藏  举报