Leetcode刷题记21-30

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

        while (l1 && l2)
        {
            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 22. 括号生成

题目类型

  1. dfs
  2. 括号匹配问题

题目链接

https://leetcode.cn/problems/generate-parentheses

思路

AC代码

class Solution &#123;
public:
    vector<string> ans;

    vector<string> generateParenthesis(int n) &#123;
        dfs(0, 0, "", n);
        return ans;
    &#125;
    // 满足lc < n 可以添加左括号
    // 满足rc < n && rc < lc 可以添加右括号
    void dfs(int lc, int rc, string seq, int n)
    &#123;
        if (lc == n && rc == n)
        &#123;
            ans.push_back(seq);
        &#125;
        else
        &#123;
            if (lc < n) dfs(lc + 1, rc, seq + '(', n);
            if (rc < n && rc < lc) dfs(lc, rc + 1, seq + ')', n);
        &#125;
    &#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(K*N)$

此处可以用小根堆做优化

  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 26. 删除排序数组中的重复项

题目类型

  1. 数组去重

题目链接

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

思路

此题比较简单

  1. 用k指向结果存储
  2. 遍历nums,每次nums[k] 和 nums[i]
  3. 如果相同说明出现重复 k不移动等待出现不重复的数将其覆盖

AC代码

class Solution &#123;
public:
    int removeDuplicates(vector<int>& nums) &#123;
        int k = 0; // k作为存储下标
        for (int i = 1; i < nums.size(); i ++)
        &#123;
            if (nums[i] != nums[k])
            &#123;
                k ++;
                nums[k] = nums[i];
            &#125;
        &#125;
        return k + 1;
    &#125;
&#125;;

LeetCode 27. 移除元素

题目类型

  1. 去重通用法
  2. 双指针

题目链接

https://leetcode.cn/problems/remove-element/

思路

跟上一题差不多,很简单,直接AC,注意上一题26是return k+1,这里是return k

或者使用双指针,前半部分存储有效数据,后半部分存储无用数据

AC代码1

class Solution &#123;
public:
    int removeElement(vector<int>& nums, int val) &#123;
        int k = 0;
        for (int i = 0; i < nums.size(); i ++)
        &#123;
            if (nums[i] != val)
            &#123;
                 nums[k] = nums[i];
                 k ++;
            &#125;
        &#125;
        return k ;
    &#125;
&#125;;

AC代码2

class Solution &#123;
public:
    int removeElement(vector<int>& nums, int val) &#123;
        int j = nums.size() - 1;
        for (int i = 0; i <= j; i ++)
        &#123;
            if (nums[i] == val) // 出现重复数 放到后面
            &#123;
                swap(nums[i], nums[j]);
                i --, j --;
            &#125;
        &#125;
        return j + 1;
    &#125;
&#125;;

LeetCode 28. 实现 strStr()

题目类型

  1. 字符串匹配
  2. kmp

题目链接

https://leetcode.cn/problems/implement-strstr/

思路

kmp算法

AC代码

class Solution &#123;
public:
    int strStr(string haystack, string needle) &#123;
        if (haystack.empty() || needle.empty()) return -1;
        int n = haystack.size(), m = needle.size();
        haystack = ' ' + haystack, needle = ' ' + needle;

        vector<int> next(m + 1);
        // 求next数组
        for (int i = 2, j = 0; i <= m; i ++)
        &#123;
            while (j && needle[i] != needle[j + 1]) j = next[j];
            if (needle[i] == needle[j + 1]) j ++;
            next[i] = j;
        &#125; 

        // kmmp
        for (int i = 1, j = 0; i <= n; i ++)
        &#123;
            while (j && haystack[i] != needle[j + 1]) j = next[j];
            if (haystack[i] == needle[j + 1]) j ++;
            if (j == m)
            &#123;
                return i -m;
            &#125;
        &#125;
        return -1;
    &#125;
&#125;;

LeetCode 29. 两数相除

题目类型

  1. 位运算

题目链接

https://leetcode.cn/problems/divide-two-integers/

思路

AC代码

class Solution &#123;
public:
    int divide(int x, int y) &#123;
        typedef long long LL;
        vector<LL> exp;
        bool is_minus = false;
        if (x < 0 && y > 0 || x > 0 && y < 0) is_minus = true;

        LL a = abs((LL)x), b = abs((LL)y);
        for (LL i = b; i <= a; i = i + i) exp.push_back(i);

        LL res = 0;
        for (int i = exp.size() - 1; i >= 0; i --)
        &#123;
            if (a >= exp[i])
            &#123;
                a -= exp[i];
                res += 1ll << i;
            &#125;
        &#125;

        if (is_minus) res = -res;

        if (res > INT_MAX || res < INT_MIN) res = INT_MAX;
        return res;
    &#125;
&#125;;

LeetCode 30. 串联所有单词的子串

题目类型

  1. 哈希表

题目链接

https://leetcode.cn/problems/substring-with-concatenation-of-all-words/

思路

参考这位大佬的讲解
https://www.acwing.com/solution/content/3669/

AC代码

class Solution &#123;
public:
    vector<int> findSubstring(string s, vector<string>& words) &#123;
        unordered_map<string, int> hash;
        vector<int> res;
        int n = s.length(), m = words.size();
        if (n == 0 || m == 0) return res;
        int len = words[0].length(), end = n - m * len; // 此时不必让
        if (n < m * len) return res;
        for (auto word:words) hash[word] ++;

        for (int i = 0; i <= end; i ++)
        &#123;
            unordered_map<string, int> cur_hash;
            int satisfy = 0;
            for (int j = i; j <= n - len; j += len)
            &#123;
                string cur = s.substr(j, len);
                if (hash.find(cur) == hash.end())
                    break;
                else
                &#123;
                    cur_hash[cur] ++;
                    if (cur_hash[cur] > hash[cur])
                        break;
                    else if (cur_hash[cur] == hash[cur])
                        satisfy++;
                    if (satisfy == hash.size())
                    &#123;
                        res.push_back(i);
                        break;
                    &#125;
                &#125;
            &#125;
        &#125;
        return res;
    &#125;

&#125;;

  转载请注明: Justin博客 Leetcode刷题记21-30

 上一篇
LeetCode刷题记31-40 LeetCode刷题记31-40
LeetCode 31. 下一个排列题目类型 数组 找规律题 题目链接 https://leetcode.cn/problems/next-permutation/ 思路建议画图用例子模拟 [2 3 5 4 1] => [2 4
下一篇 
408相关算法刷题【栈、队列篇】 408相关算法刷题【栈、队列篇】
LeetCode 20. 有效的括号题目类型 栈 题目链接 https://leetcode.cn/problems/valid-parentheses/ 思路方法一: 使用栈存储,遇到( { [ 压入栈 遇到) } ] 查看栈顶是否
  目录