Processing math: 100%
408相关算法刷题【链表篇】

前言

LeetCode 2. 两数相加(**)

题目类型

  1. 链表

题目链接

https://leetcode.cn/problems/add-two-numbers/

思路

跟加法逻辑一下 保存进位carry

AC代码

/**
 * 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* res = new ListNode(-1);
        ListNode* cur = res;

        int carry = 0;
        while(l1 || l2)
        {
            int n1 = l1 ? l1->val : 0;
            int n2 = l2 ? l2->val : 0;
            int sum = carry + n1 + n2;
            carry = sum / 10;
            cur -> next = new ListNode(sum % 10);
            cur = cur -> next;
            if (l1) l1 = l1 -> next;
            if (l2) l2 = l2 -> next;    
        }
        if (carry) cur -> next = new ListNode(carry);
        return res->next;
    }
};

LeetCode 19. 删除链表的倒数第N个节点(**)

题目类型

  1. 链表的删除
  2. 双指针应用

题目链接

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

思路

注意加个头结点防止只有一个元素,而要删除该元素
方法一:数学方法

  1. p指向头结点
  2. 让p走k - n - 1步即可到达删除结点的前驱,其中k为链表长度

方法二:双指针(时间更快)

  1. 都指向头结点
  2. q走n + 1步
  3. p q同时走 当q到达尾部NULL时候,p指向倒数第n个数的前驱

AC代码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) {
        ListNode* dummy_head = new ListNode(-1);
        dummy_head->next = head;

        int k = 0;
        auto p = dummy_head;
        while(p->next)
        {
            k ++;
            p = p->next;
        }
        auto q = dummy_head;
        for (int i = 0; i < k - n; i ++) q = q->next;
        q->next = q->next->next;
        return dummy_head->next;
    &#125;
&#125;;

AC代码2

方法二:时间更快

/**
 * Definition for singly-linked list.
 * struct ListNode &#123;
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) &#123;&#125;
 *     ListNode(int x) : val(x), next(nullptr) &#123;&#125;
 *     ListNode(int x, ListNode *next) : val(x), next(next) &#123;&#125;
 * &#125;;
 */
class Solution &#123;
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) &#123;
        ListNode* dummy_head = new ListNode(-1);
        dummy_head->next = head;

        ListNode* p = dummy_head;
        ListNode* q = dummy_head;

        // 先让q走n + 1步 因为前面有虚拟结点
        for (int i = 0; i < n + 1; i ++) q = q->next;

        while (q)
        &#123;
            p = p->next;
            q = q->next;
        &#125;
        p->next = p->next->next;
        return dummy_head->next;
    &#125;
&#125;;

LeetCode 21. 合并两个有序链表(*)

题目类型

  1. 链表
  2. 升序序列的合并问题

题目链接

https://leetcode.cn/problems/merge-two-sorted-lists/

思路

  1. 遍历两个链表,每次选择最小的数添加到新链表的尾部即可
  2. 最后两个链表可能存在一个还有未插入序列,将该序列插入即可,这里有两种写法,一种是我自己写的不够精简,y总的写法比较精简(有时候看y总代码真是有种看艺术品感觉)

    while (l1) cur->next = l1, l1 = l1->next, cur = cur->next;
    while (l2) cur->next = l2, l2 = l2->next, cur = cur->next;

cur->next = (l1 == NULL ? l2 : l1);

AC代码

/**
 * Definition for singly-linked list.
 * struct ListNode &#123;
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) &#123;&#125;
 *     ListNode(int x) : val(x), next(nullptr) &#123;&#125;
 *     ListNode(int x, ListNode *next) : val(x), next(next) &#123;&#125;
 * &#125;;
 */
class Solution &#123;
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) &#123;
        ListNode* dummy = new ListNode(-1);
        ListNode* cur = dummy;

        while (l1 && l2)
        &#123;
            if (l1->val < l2->val)
            &#123;
                cur->next = l1;
                l1 = l1->next;
            &#125;
            else
            &#123;
                cur->next = l2;
                l2 = l2->next;
            &#125;
            cur = cur->next;
        &#125;
        // while (l1) cur->next = l1, l1 = l1->next, cur = cur->next;
        // while (l2) cur->next = l2, l2 = l2->next, cur = cur->next;

        cur->next = (l1 == NULL ? l2 : l1);
        return dummy->next;
    &#125;
&#125;;

LeetCode 23. 合并K个排序链表(***)

题目类型

  1. 链表
  2. 小根堆
  3. k路合并问题

题目链接

https://leetcode.cn/problems/merge-k-sorted-lists/

思路

此题和21题类似,只不过21题是2路合并,该题是k路合并,思路如下:

  1. 每次取k个链表中首部最小的那个数
  2. 将该数插到结果链表
  3. 直到所有链表都插入完
    很容易得知该时间复杂度为O(KN)

此处可以用小根堆做优化

  1. 初始时将每个链表的首部放入堆中
  2. 每次取堆顶,并把堆顶t 做一次pop()
  3. 将链表尾部指向t,并若t->next不为空,继续将t->next放入堆中
    该时间复杂度为O(Nlogk)

    AC代码

/**
 * Definition for singly-linked list.
 * struct ListNode &#123;
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) &#123;&#125;
 *     ListNode(int x) : val(x), next(nullptr) &#123;&#125;
 *     ListNode(int x, ListNode *next) : val(x), next(next) &#123;&#125;
 * &#125;;
 */
class Solution &#123;
public:
    struct Cmp
    &#123;
        bool operator()(ListNode* a, ListNode* b)
        &#123;
            return a->val > b->val;
        &#125;
    &#125;;

    ListNode* mergeKLists(vector<ListNode*>& lists) &#123;
        priority_queue<ListNode*, vector<ListNode*>, Cmp> heap;
        auto dummy = new ListNode(-1), p = dummy;
        // 将每个链表的第一个元素(也就是最小数)放入堆中
        for (auto l : lists) if (l) heap.push(l);

        while (heap.size())
        &#123;
            // 取堆顶
            auto t = heap.top();
            heap.pop();

            p->next = t;
            p = t;

            if (t->next) heap.push(t->next);
        &#125;

        return dummy->next;
    &#125;
&#125;;

LeetCode 24. 两两交换链表中的节点(**)

题目类型

  1. 链表
  2. 交换问题

题目链接

https://leetcode.cn/problems/swap-nodes-in-pairs/

思路

对于p->a->b,其中p为虚拟头结点
交换ab只需要

p->next = b;
a->next = b->next;
b->next = a;

AC代码

/**
 * Definition for singly-linked list.
 * struct ListNode &#123;
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) &#123;&#125;
 *     ListNode(int x) : val(x), next(nullptr) &#123;&#125;
 *     ListNode(int x, ListNode *next) : val(x), next(next) &#123;&#125;
 * &#125;;
 */
class Solution &#123;
public:
    ListNode* swapPairs(ListNode* head) &#123;
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        auto p = dummy;
        while (p->next && p->next->next)
        &#123;
            auto a = p->next, b = a->next;

            p->next = b;
            a->next = b->next;
            b->next = a;
            p = a;
        &#125;
        return dummy->next;
    &#125;
&#125;;

LeetCode 25. K 个一组翻转链表(***)

题目类型

  1. 链表
  2. k组翻转

题目链接

https://leetcode.cn/problems/reverse-nodes-in-k-group/

思路

此时看起来不难,实际实现的时候很容易出错,建议多刷几次

  1. 每次检验是否下一组足够k个元素,不够则break 够的话继续往下执行
  2. 对于 p->a->b->c->d 从a开始进行k-1次循环反转指针,而且注意在反转指针之前要把b->next存起来

    auto c = b->next, b->next = a

  3. k-1次之后(也就是该组已经反转成功后) 此时将p->next->next = b,落到了下一组以b为开始的反转的前一个结点(可以看做是虚拟结点)
  4. 继续以上操作

AC代码

/**
 * Definition for singly-linked list.
 * struct ListNode &#123;
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) &#123;&#125;
 *     ListNode(int x) : val(x), next(nullptr) &#123;&#125;
 *     ListNode(int x, ListNode *next) : val(x), next(next) &#123;&#125;
 * &#125;;
 */
class Solution &#123;
public:
    ListNode* reverseKGroup(ListNode* head, int k) &#123;
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;

        for (auto p = dummy;;)
        &#123;
            // 判断是否满足k个元素
            auto q = p;
            for (int i = 0; i < k && q; i ++) q = q->next;
            if (!q) break; // 不满足直接跳出


            // 满足有k个元素
            auto a = p->next, b = a->next;
            for (int i = 0; i < k - 1; i ++) // 内部交换k - 1次
            &#123;
                auto c = b->next;
                b->next = a;
                a = b, b = c;
            &#125;
            auto t = p->next;
            p->next = a;
            t->next = b;
            p = t;
        &#125;
        return dummy->next;
    &#125;
&#125;;

LeetCode 82. 删除排序链表中的重复元素 II

题目类型

  1. 删除操作

题目链接

https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/

思路

这里解法一是自己写的方法

解法二是y总的,y总代码比较精简,y总的题解:https://www.acwing.com/solution/content/166/

AC代码一

// 朴素法
class Solution &#123;
public:
    ListNode* deleteDuplicates(ListNode* head) &#123;
        if (!head) return head;

        // 创建虚拟头结点
        ListNode *dummy = new ListNode(-1);
        dummy->next = head;
        ListNode *cur = dummy;

        // 遍历
        while (cur->next && cur->next->next)
        &#123;
            if (cur->next->val == cur->next->next->val)
            &#123;
                int x = cur->next->val;
                while (cur->next && cur->next->val == x)
                &#123;
                    cur->next = cur->next->next;
                &#125;    
            &#125;
            else
            &#123;
                cur = cur->next;
            &#125;
        &#125;
        return dummy->next;
    &#125;
&#125;;

AC代码二

class Solution &#123;
public:
    ListNode* deleteDuplicates(ListNode* head) &#123;
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        ListNode* p = dummy;

        while (p->next)
        &#123;
            ListNode* q = p->next->next;
            while (q && q->val == p->next->val) q = q->next;
            if (p->next->next == q) p = p->next;
            else p->next = q;
        &#125;
        return dummy->next;
    &#125;
&#125;;

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

题目类型

  1. 删除

题目链接

https://leetcode.cn/problems/remove-duplicates-from-sorted-list/submissions/

思路

比较简单看代码就可以

AC代码

class Solution &#123;
public:
    ListNode* deleteDuplicates(ListNode* head) &#123;
        ListNode* p = head;
        while (p)
        &#123;
            ListNode* q = p->next;
            if (q && q->val == p->val)
            &#123;
                p->next = q->next;
            &#125;
            else
            &#123;
                p = p->next;
            &#125;
        &#125;
        return head;
    &#125;
&#125;;

LeetCode 86. 分隔链表

题目类型

  1. 分治

题目链接

https://www.acwing.com/activity/content/problem/content/2435/

思路

  1. 定义两个链表,一个存储小于x的数,一个存储大于x的数
  2. 从头往后遍历分别插入两个链表
  3. 最后将两个链表归并

AC代码

class Solution &#123;
public:
    ListNode* partition(ListNode* head, int x) &#123;
        // 定义两个头结点
        ListNode* l_head = new ListNode(-1), *r_head = new ListNode(-1);
        ListNode* lt = l_head, *rt = r_head;

        for (auto p = head; p; p = p->next)
        &#123;
            if (p->val < x)
            &#123;
                lt->next = p;
                lt = p;
            &#125;
            else
            &#123;
                rt->next = p;
                rt = p;
            &#125;
        &#125;
        lt->next = r_head->next;
        rt->next = nullptr;
        return l_head->next;
    &#125;
&#125;;

LeetCode 92. 反转链表 II

题目类型

  1. 链表逆转

题目链接

https://leetcode.cn/problems/reverse-linked-list-ii/

思路

这道题一点点绕 我刚开始也想不出来 第一天大概写了一下然后报错 我就跳过写第二题了
代码思路看下面AC代码的注释即可

AC代码

class Solution &#123;
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) &#123;
        if (left == right) return head;
        //创造头结点
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        ListNode* p = dummy;

        // p找到left的前驱结点 a为left结点的前驱结点 b为left结点 c为left结点的下个结点
        for (int i = 0; i < left - 1; i ++) p = p->next;
        ListNode* a = p, *b = a ->next, *c = b->next;

        // 将left到right的指针反向
        for (int i = left; i < right; i ++) // 内部进行right-left次逆转即可
        &#123;
            ListNode* d = c->next;// 这里要保存好c的next指针 因为c要反向指向b 会丢失下一个结点
            c->next = b;
            // 更新下一次翻转所需要的工作指针
            b = c, c = d;
        &#125;
        // 从left 到 right 已经反转 接下来处理好原来left的前驱和right的后继
        a->next->next = c; // 此时c是right的下一个结点 只需要把left点(即a->next)的next指向c即可
        a->next = b; // 此时b是right点 只需要把left的前驱结点(即a)的next指向b 

        return dummy->next;
    &#125;
&#125;;

LeetCode 109. 有序链表转换二叉搜索树

题目类型

  1. 二叉树

题目链接

https://leetcode.cn/problems/convert-sorted-list-to-binary-search-tree/

思路

每次以链表的中点为根节点,生成二叉树,可以递归处理

AC代码

class Solution &#123;
public:
    // 寻找链表的(left, right)中点 用快慢指针
    ListNode* getMidNode(ListNode* left, ListNode* right)
    &#123;
        ListNode* fast = left;
        ListNode* slow = left;
        while (fast != right && fast->next != right)
        &#123;
            fast = fast->next->next;
            slow = slow->next;
        &#125;
        return slow;
    &#125;

    // 以left和right的中点建立一棵二叉子树
    TreeNode* buildBST(ListNode* left, ListNode* right)
    &#123;
        if (left == right) return nullptr;

        // 寻找left到right的中点
        ListNode* mid = getMidNode(left, right);
        // 以mid为中点转变为一棵二叉树
        TreeNode* root = new TreeNode(mid->val);
        root->left = buildBST(left, mid);
        root->right = buildBST(mid->next, right);
        return root;
    &#125;

    TreeNode* sortedListToBST(ListNode* head) &#123;
        return buildBST(head, nullptr);
    &#125;
&#125;;

LeetCode 114. 二叉树展开为链表

题目类型

  1. 链表二叉树

题目链接

https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/

思路

看LeetCode的题解就好了 很详细

https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/solution/er-cha-shu-zhan-kai-wei-lian-biao-by-leetcode-solu/

AC代码一 遍历树

class Solution &#123;
public:
    void flatten(TreeNode* root) &#123;
        while (root)
        &#123;
            TreeNode* p = root->left;
            if (p)
            &#123;
                TreeNode* q = p;
                while (q->right) q = q->right;
                q->right = root->right;
                root->right = p;
                root->left = nullptr;
            &#125;
            root = root->right;
        &#125;
    &#125;
&#125;;

AC代码二 先序遍历


LeetCode 116. 填充每个节点的下一个右侧节点指针

题目类型

  1. BFS

题目链接

https://leetcode.cn/problems/populating-next-right-pointers-in-each-node/submissions/

思路

  1. 遍历二叉树的每一层
  2. 对于每一层的结点A和下一个结点B,将A的左孩子的next指向A的右孩子,如果B存在,将A的右孩子指向B的左孩子

AC代码

class Solution &#123;
public:
    Node* connect(Node* root) &#123;
        if (!root) return root;
        // 先存下根节点
        Node* source = root;
        // 遍历树的每一层
        while (root->left)
        &#123;
            // 遍历每一层的每一个结点
            for (Node* p = root; p; p = p->next)
            &#123;
                // 同一父节点 直接让左孩子指向右孩子
                p->left->next = p->right;
                // 不同父节点 让前一个父节点的右孩子指向后一个父节点的左孩子
                // 如果没有下一个结点则无需添加
                if (p->next) p->right->next = p->next->left;
                else p->right->next = NULL;
            &#125;
            root = root->left;
        &#125;
        return source;
    &#125;
&#125;;

LeetCode 117. 填充每个节点的下一个右侧节点指针 II

题目类型

  1. BFS

题目链接

https://leetcode.cn/problems/populating-next-right-pointers-in-each-node-ii

思路

与前一题不同,该题遍历每一层的时候需要创建headtail两个指针
head是用来使下一层的结点连接到一条链表中,tail是用来指向该链表的尾结点用于插入
最后更新下一层 cur

AC代码

class Solution &#123;
public:
    Node* connect(Node* root) &#123;
        if (!root) return root;
        Node* cur = root;
        while (cur)
        &#123;
            Node* head = new Node(-1);
            Node* tail = head;
            // 遍历该层的每一个结点
            for (Node* q = cur; q; q = q->next)
            &#123;
                if (q->left) tail = tail->next = q->left;
                if (q->right) tail = tail->next = q->right;
            &#125;
            cur = head->next;
        &#125;
        return root;
    &#125;
&#125;;

LeetCode 141. 环形链表

题目类型

  1. 判断环

题目链接

https://leetcode.cn/problems/linked-list-cycle

思路

方法一:
遍历链表,使用哈希表记录访问过的点,如果访问的点出现在哈希表中说明存在环

方法二:
快慢指针,如果slow 和 fast相遇说明存在环

AC代码—哈希表

class Solution &#123;
public:
          bool hasCycle(ListNode *head) &#123;
        // 使用哈希表存储访问过的点
        unordered_set<ListNode*> hash;

        ListNode* p = head;
        while (p)
        &#123;
            if (hash.count(p)) return true;
            hash.insert(p);
            p = p->next;
        &#125;
        return false;
    &#125;
&#125;;

AC代码—快慢指针

class Solution &#123;
public:
    bool hasCycle(ListNode *head) &#123;
        if (!head || !head->next) return false;
        ListNode* slow = head, * fast = head->next;
        while (slow != fast)
        &#123;
            if (!fast || !fast->next) return false;
            fast = fast->next->next;
            slow = slow->next;
        &#125;
        return true;
    &#125;
&#125;;

LeetCode 143. 重排链表

题目类型

  1. 链表排序

题目链接

https://leetcode.cn/problems/reorder-list/submissions/

思路

方法一:

  1. 由于链表没有随机访问的特性,所以将链表存储在线性表r
  2. 使用双指针对链表重新排序

时间复杂度O(n) 空间复杂度O(n)

方法二:此题可以分为三个子问题

  1. 寻找链表中间 参考 LeetCode 876
  2. 反转后半段的链表 参考 LeetCode 206
  3. 使用两个指针,交叉拼接拼接两个链表

AC代码一

class Solution &#123;
public:
    void reorderList(ListNode* head) &#123;
        if (!head) return ;
        vector<ListNode*> r;

        while (head)
        &#123;
            r.push_back(head);
            head = head->next;
        &#125;

        int i = 0, j = r.size() - 1;
        while (i < j)
        &#123;
            r[i ++]->next = r[j];

            if (i == j) break;

            r[j --]->next = r[i];
        &#125;
        r[i]->next = nullptr;
    &#125;
&#125;;

AC代码二

class Solution &#123;
public:
    // 寻找链表的中点
    ListNode* searchMid(ListNode* head)
    &#123;
        ListNode* slow = head;
        ListNode* fast = head;
        while (fast && fast->next)
        &#123;
            slow = slow->next;
            fast = fast->next->next;
        &#125;
        return slow;
    &#125;

    // 反转链表
    ListNode* reverseList(ListNode* head)
    &#123;
        ListNode* prev = nullptr;
        ListNode* cur = head;
        while (cur)
        &#123;
            ListNode* curTmp = cur->next;
            cur->next = prev;
            // 保存好下一次反转的前驱结点和下一次反转的当前节点cur
            prev = cur, cur = curTmp;
        &#125;
        return prev;
    &#125;

    // 交叉合并链表
    void mergeList(ListNode* l1, ListNode* l2)
    &#123;
        while(l1 && l2)
        &#123;
            ListNode* l1_tmp = l1->next;
            ListNode* l2_tmp = l2->next;

            l1->next = l2, l1 = l1_tmp;
            l2->next = l1, l2 = l2_tmp;
        &#125;
    &#125;

    void reorderList(ListNode* head) &#123;
        ListNode* mid = searchMid(head);
        // 划分两个链表
        ListNode* l1 = head;
        ListNode* l2 = mid->next;
        mid->next = nullptr;

        // 先反转链表
        l2 = reverseList(l2);

        // 再合并链表
        mergeList(l1, l2);
    &#125;
&#125;;

LeetCode 146. LRU缓存机制

题目类型

  1. 链表插入
  2. 链表删除
  3. 双链表
  4. 哈希表

题目链接

https://leetcode.cn/problems/lru-cache

思路

此题难度稍微一点点大,主要是题目要求getputO(1),也就是说插入删除查找要O(1) 复杂度,所以利用哈希表和双链表来实现缓存的LRU

  1. 可以利用哈希表当做缓存,查找可以实现O(1)
  2. 双链表当做缓存的时间队列(最近访问放最前面,最后访问放最后),插入和删除由于有头指针和尾指针,也可以实现O(1)

其他就是常见的业务需求了

AC代码

class LRUCache &#123;
public:
    struct Node
    &#123;
        int key, val;
        Node* left, *right;
        Node(int _key, int _val): key(_key), val(_val), left(NULL), right(NULL) &#123;&#125;;
    &#125; *L, *R;
    unordered_map<int, Node*> hash;

    void insert(Node *p)
    &#123;
        p->right = L->right;
        p->left  = L;
        L->right->left = p;
        L->right = p;
    &#125;

    void remove(Node* p)
    &#123;
        p->right->left = p->left;
        p->left->right = p->right;
    &#125;
    int n;
    LRUCache(int capacity) &#123;
        n = capacity;
        L = new Node(-1, -1), R = new Node(-1, -1);
        L->right = R;
        R->left = L;
    &#125;

    int get(int key) &#123;
        // 若不在缓存
        if (hash.count(key) == 0) return -1;

        auto p = hash[key];
        remove(p), insert(p);
        return p->val;

    &#125;

    void put(int key, int value) &#123;
        // 若在缓存中 覆盖val值 并更新链表
        if (hash.count(key))
        &#123;
            auto p = hash[key];
            p->val = value;
            remove(p), insert(p);
        &#125;
        else
        &#123;
            if (hash.size() == n) // 如果缓存满了 删除最少访问的那个
            &#123;
                auto p = R->left;
                remove(p);
                hash.erase(p->key);
            &#125;
            auto p = new Node(key, value);
            insert(p), hash[key] = p;
        &#125;
    &#125;
&#125;;

LeetCode 147. 对链表进行插入排序

题目类型

  1. 插入排序

题目链接

https://leetcode.cn/problems/insertion-sort-list

思路

看起来简单,我写的时候容易被指针搞乱,基础还是不扎实

  1. 创建虚拟结点dummy,用于存放结果
  2. p指针遍历原链表,cur遍历结果链表
  3. 寻找第一个比p大的结点,将cur插在p的前面

AC代码

class Solution &#123;
public:
    ListNode* insertionSortList(ListNode* head) &#123;
        ListNode* dummy = new ListNode(-1);

        // 遍历链表
        for (auto p = head; p;)
        &#123;
            // 从结果链表的开头也就是dummy开始往后寻找
            auto cur = dummy, p_next_tmp = p->next;
            // 寻找结果链表的第一个大于p的结点 将p放在该结点的前面
            while (cur->next && p->val >= cur->next->val) cur = cur->next;
            p->next = cur ->next;
            cur->next = p;
            p = p_next_tmp;
        &#125;
        return dummy->next;
    &#125;
&#125;;

LeetCode 148. 排序链表

题目类型

  1. 归并

题目链接

https://leetcode.cn/problems/sort-list/

思路

先跳过吧 这题我不会做。。。

AC代码


LeetCode 160. 相交链表

题目类型

  1. 技巧题

题目链接

https://leetcode.cn/problems/intersection-of-two-linked-lists/

思路

AC代码

class Solution &#123;
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) &#123;
        ListNode* p = headA, *q = headB;
        while (p != q)
        &#123;
            if (p) p = p->next;
            else p = headB;
            if (q) q = q->next;
            else q = headA;
        &#125;
        return p;
    &#125;
&#125;;

LeetCode 203. 移除链表元素

题目类型

  1. 链表删除

题目链接

https://leetcode.cn/problems/remove-linked-list-elements/

思路

思路简单 找到删除点 用链表删除就可以了
注意一下删除节点是head的情况 新创建一个虚拟头结点即可

AC代码

class Solution &#123;
public:
    ListNode* removeElements(ListNode* head, int val) &#123;
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        for (auto p = dummy; p;)
        &#123;
            if (p->next && p->next->val == val) 
                p->next = p->next->next;
            else
                p = p->next;
        &#125;
        return dummy->next;
    &#125;
&#125;;

LeetCode 206. 反转链表

题目类型

  1. 迭代

题目链接

https://leetcode.cn/problems/reverse-linked-list/

思路

画图看比较简单
遍历链表,每次记录好cur结点的后继结点,再将cur指向cur的前驱结点,以此迭代下去

AC代码

class Solution &#123;
public:
    ListNode* reverseList(ListNode* head) &#123;
        ListNode* prev = nullptr;
        ListNode* cur  = head;
        while (cur)
        &#123;
            ListNode* curNextTmp = cur->next;
            cur->next = prev;
            prev = cur, cur = curNextTmp;
        &#125;
        return prev;
    &#125;
&#125;;

LeetCode 234. 回文链表

题目类型

  1. 反转链表

题目链接

https://leetcode.cn/problems/palindrome-linked-list/

思路

参考 LeetCode 206. 反转链表

  1. 链表的后半部分反转
  2. 使用双指针从链头和链尾往前判断
  3. 最后记得把链表恢复原状

时间复杂度O(n)
空间复杂度O(1)

AC代码

class Solution &#123;
public:
    bool isPalindrome(ListNode* head) &#123;
        int n = 0;
        for (auto p = head; p; p = p->next) n ++;
        if (n <= 1) return true;
        int half = n / 2;

        // 反转链表
        ListNode* a = head;
        for (int i = 0; i < n - half; i ++) a = a->next; // 将指针a指向翻转点
        ListNode* b = a->next;
        while (b)
        &#123;
            ListNode* c = b->next;
            b->next = a;
            a = b, b = c;
        &#125;

        // 判断是否是回文数
        bool success = true;
        ListNode* p = head, * q = a;
        for (int i = 0; i < half; i ++) // 比较half次
        &#123;
            if (p->val != q->val)
            &#123;
                success = false;
                break;
            &#125;
            p = p->next, q = q->next;
        &#125;

        // 恢复链表
        ListNode* tail = a;
        b = a->next;
        for (int i = 0;i < half - 1; i ++)
        &#123;
            ListNode* c = b->next;
            b->next = a;
            a = b, b = c;
        &#125;
        tail->next = nullptr;
        return success;
    &#125;
&#125;;

LeetCode 237. 删除链表中的节点

题目类型

  1. 删除

题目链接

https://leetcode.cn/problems/delete-node-in-a-linked-list/

思路

两行代码就可以

  1. 由于无法知道前驱结点 所以将下一个结点的值复制到当前节点
  2. 再删除下一个结点即可

AC代码

class Solution &#123;
public:
    void deleteNode(ListNode* node) &#123;
        node->val = node->next->val;
        node->next = node->next->next;
    &#125;
&#125;;

LeetCode 876. 链表的中间结点

题目类型

  1. 快慢指针

题目链接

https://leetcode.cn/problems/middle-of-the-linked-list/

思路

比较简单 双指针问题 直接看代码就行

AC代码

class Solution &#123;
public:
    ListNode* middleNode(ListNode* head) &#123;
        ListNode* slow = head;
        ListNode* fast = head;
        while (fast && fast->next) &#123;
            slow = slow->next;
            fast = fast->next->next;
        &#125;
        return slow;
    &#125;
&#125;;

LeetCode 328. 奇偶链表

题目类型

  1. 插入
  2. 合并

题目链接

https://leetcode.cn/problems/odd-even-linked-list

思路

分为两个链表,最后拼接即可

AC代码

class Solution &#123;
public:
    ListNode* oddEvenList(ListNode* head) &#123;
        // 只有一个或两个元素直接返回原链表
        if(!head || !(head->next)) return head;

        // 定义奇数链表和偶数的链表的头结点和尾结点
        ListNode* oh = head, * ot = oh;
        ListNode* eh = head->next, * et = eh;

        for (ListNode* p = head->next->next; p;)
        &#123;
            ot = ot->next = p;
            p = p->next;
            if (p)
            &#123;
                et = et->next = p;
                p = p->next;
            &#125;
        &#125;
        ot->next = eh;
        et->next = nullptr;
        return oh;
    &#125;
&#125;;

LeetCode 445. 两数相加 II

题目类型

  1. 高精度加法

题目链接

https://leetcode.cn/problems/add-two-numbers-ii/

思路

由于这是链表,又为了人为习惯故意让结点从高位到低位,但不利于计算,所以这里需要反转链表

  1. 反转两个链表l1 l2
  2. 依次遍历两个链表,使用进位变量c,存储同一位的两个结点相加的结果
  3. 将结果取余放入结果链表中,进位变量c也要修改

AC代码

class Solution &#123;
public:
    ListNode* reverse(ListNode* head)
    &#123;
        ListNode* prev = nullptr, *cur = head;
        while (cur)
        &#123;
            ListNode* cur_tmp_next = cur->next;
            cur->next = prev;
            prev = cur, cur = cur_tmp_next;
        &#125;
        return prev;
    &#125;

    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) &#123;
        l1 = reverse(l1), l2 = reverse(l2);

        // 存放结果
        ListNode* res_dummy = new ListNode(-1);
        int c = 0;
        while (l1 || l2 || c)
        &#123;
            if (l1) c += l1->val, l1 = l1->next;
            if (l2) c += l2->val, l2 = l2->next;
            ListNode* cur = new ListNode(c % 10);
            c /= 10;
            // 使用头插法
            cur->next = res_dummy->next;
            res_dummy->next = cur;
        &#125;
        return res_dummy->next;
    &#125;
&#125;;

LeetCode 725. 分隔链表

题目类型

  1. 分段

题目链接

https://leetcode.cn/problems/split-linked-list-in-parts/

思路

将n个结点的链表分为k份, n % k == c

  1. 如果c == 0,说明刚刚好分,或者前面几个链表有结点,最后几个没有结点(即k> n的情况)
  2. 如果c != 0,则每个链表分配len = n/k结点外,前c个链表再分配一个结点

AC代码

class Solution &#123;
public:
    vector<ListNode*> splitListToParts(ListNode* head, int k) &#123;
        int n = 0;
        for (auto p = head; p; p = p->next) n ++;

        vector<ListNode*> res;
        auto p = head;

        for (int i = 0; i < k; i ++)
        &#123;
            // 当前链表长度len
            int len = n / k;
            // 前n % k个链表多1个
            if (i + 1 <= n % k) len ++;

            res.push_back(p);
            for (int j = 0; j < len - 1; j ++) p = p->next;

            if (p)
            &#123;
                auto q = p->next;
                p->next = nullptr;
                p = q;
            &#125;
        &#125;
        return res;
    &#125;
&#125;;

##

题目类型

  1. 题目链接

思路

AC代码


##【未完待更】


Related Issues not found

Please contact @AlexJakin to initialize the comment

 上一篇
408相关算法刷题【栈、队列篇】 408相关算法刷题【栈、队列篇】
LeetCode 20. 有效的括号题目类型 栈 题目链接 https://leetcode.cn/problems/valid-parentheses/ 思路方法一: 使用栈存储,遇到( { [ 压入栈 遇到) } ] 查看栈顶是否
下一篇 
【算法基础篇】DFS和BFS及其经典习题 【算法基础篇】DFS和BFS及其经典习题
DFS深搜深搜解决排列问题,也是一个很好的模板 问题给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法。 AC#include <iostream> using namespace std; const int N =
  目录
  1. 前言
  2. LeetCode 2. 两数相加(**)
  3. LeetCode 19. 删除链表的倒数第N个节点(**)
  4. LeetCode 21. 合并两个有序链表(*)
  5. LeetCode 23. 合并K个排序链表(***)
  6. LeetCode 24. 两两交换链表中的节点(**)
  7. LeetCode 25. K 个一组翻转链表(***)
  8. LeetCode 82. 删除排序链表中的重复元素 II
  9. LeetCode 83. 删除排序链表中的重复元素
  10. LeetCode 86. 分隔链表
  11. LeetCode 92. 反转链表 II
  12. LeetCode 109. 有序链表转换二叉搜索树
  13. LeetCode 114. 二叉树展开为链表
  14. LeetCode 116. 填充每个节点的下一个右侧节点指针
  15. LeetCode 117. 填充每个节点的下一个右侧节点指针 II
  16. LeetCode 141. 环形链表
  17. LeetCode 143. 重排链表
  18. LeetCode 146. LRU缓存机制
  19. LeetCode 147. 对链表进行插入排序
  20. LeetCode 148. 排序链表
  21. LeetCode 160. 相交链表
  22. LeetCode 203. 移除链表元素
  23. LeetCode 206. 反转链表
  24. LeetCode 234. 回文链表
  25. LeetCode 237. 删除链表中的节点
  26. LeetCode 876. 链表的中间结点
  27. LeetCode 328. 奇偶链表
  28. LeetCode 445. 两数相加 II
  29. LeetCode 725. 分隔链表