蒟蒻的LeetCode刷题记录21~30

本文详细介绍了几种常见的链表操作,包括合并两个有序链表、括号生成、合并K个升序链表、两两交换链表中的节点、K个一组翻转链表、删除排序数组中的重复项以及移除元素等。这些操作均采用了递归、迭代和堆优化等算法,展示了链表操作的基本技巧和复杂情况下的解决方案。同时,文章还涉及到了字符串匹配的KMP算法和快速幂思想在除法中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

21. 合并两个有序链表
跟之前剑指的题是一样的,直接归并。

/**
 * 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) {
        auto dummy = new ListNode(-1), tail = dummy;
        while (l1 && l2) {
            if (l1->val < l2->val) {
                tail = tail->next = l1;
                l1 = l1->next;
            }
            else {
                tail = tail->next = l2;
                l2 = l2->next;
            }
        }
        if (l1) tail->next = l1;
        if (l2) tail->next = l2;

        return dummy->next;
    }
};

22. 括号生成
这道题其实是一个经典的卡特兰数问题
在这里插入图片描述

class Solution {
public:

    vector<string> ans;

    vector<string> generateParenthesis(int n) {
        dfs(n, 0, 0, "");
        return ans;
    }

    void dfs(int n, int lc, int rc, string seq) {
        if (lc == n && rc == n) ans.push_back(seq);
        else {
            if (lc < n) dfs(n, lc + 1, rc, seq + '(');
            if (rc < n && lc > rc) dfs(n, lc, rc + 1, seq + ')');
        }
    }
};

23. 合并K个升序链表
跟之前两个链表合并的思路一样,还是基于归并的思想,这题用堆进行优化。
所有STL容器和库函数默认使用的是小于号,所以STL中的优先队列默认是大根堆。如果加上greater<>参数,那么会默认使用大于号。在下面的代码中优先队列会默认用一对小括号表示小于号,并且默认会构造一个大根堆,所以我们把小括号里的关系变一下,最后就可以得到小根堆了。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    //重载(),为了构造下面的小根堆
    struct Cmp {
        bool operator() (ListNode* a, ListNode* b) {
            return a->val > b->val;
        }
    };

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        priority_queue<ListNode*, vector<ListNode*>, Cmp> heap;//小根堆
        auto dummy = new ListNode(-1), tail = dummy;//虚拟头结点,尾节点
        for (auto l : lists) if (l) heap.push(l);//非空的话就插入节点
        //k路归并,每次找最小值
        while (heap.size()) {
            auto t = heap.top();//找堆顶元素(最小值)
            heap.pop();//删去堆顶元素

            tail->next = t;//将当前节点插到尾节点的后面
            tail = tail->next;//更新尾节点
            if (t->next) heap.push(t->next);//将下一个节点插入堆中,相当于指针后移一位
        }
        return dummy->next;
    }
};

24. 两两交换链表中的节点
在这里插入图片描述

/**
 * 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* swapPairs(ListNode* head) {
        auto dummy = new ListNode(-1);
        dummy->next = head;
        for (auto cur = dummy; cur->next && cur->next->next; ) {
            auto first = cur->next, second = first->next;
            cur->next = second;
            first->next = second->next;
            second->next = first;
            cur = first;
        }
        return dummy->next;
    }
};

25. K 个一组翻转链表
链表题指来指去的一定要画图才能清晰。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        auto dummy = new ListNode(-1);
        dummy->next = head;
        for (auto p = dummy;;) {
            auto q = p;
            for (int i = 0; i < k && q; i ++ ) q = q->next;
            if (!q) break;
            auto a = p->next, b = a->next;
            for (int i = 0; i < k - 1; i ++ ) {
                auto c = b->next;
                b->next = a;
                a = b, b = c;
            }
            auto c = p->next;
            p->next = a, c->next = b;
            p = c;
        }
        return dummy->next;
    }
};

26. 删除排序数组中的重复项
经典的双指针算法,同样也是c++里的unique函数

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int k = 0;//k是前面慢的指针,i是后面快的指针
        for (int i = 0; i <nums.size(); i++) {
            if (!i || nums[i] != nums[i - 1])//将i = 0的情况特判,所以加上'!i'
                nums[k++] = nums[i];
        }
        return k;
    }
};

27. 移除元素
和上一题是完全一样的题目:

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

28. 实现 strStr()

一道KMP算法的裸题,直接KMP模板。
题解,暴力匹配或者kmp

class Solution {
public:
    int strStr(string haystack, string needle) {
        int n = haystack.length(), m = needle.length();
        for (int i = 0; i < n - m + 1; i++) {
            bool ok = true;
            for (int j = 0; j < m; j++)
                if (haystack[i + j] != needle[j]) {
                    ok = false;
                    break;
                }
            if (ok)
                return i;
        }
        return -1;
    }
};
class Solution {
public:
    int strStr(string haystack, string needle) {
        int n = haystack.length(), m = needle.length();
        if (m == 0)
            return 0;
        vector<int> next(m);
        next[0] = -1;
        int j = -1;
        for (int i = 1; i < m; i++) {
            while (j > -1 && needle[i] != needle[j + 1]) j = next[j];
            if (needle[i] == needle[j + 1]) j++;
            next[i] = j;
        }

        j = -1;
        for (int i = 0; i < n; i++) {
            while (j > -1 && haystack[i] != needle[j + 1]) j = next[j];
            if (haystack[i] == needle[j + 1]) j++;
            if (j == m - 1)
                return i - m + 1;
        }
        return -1;
    }
};

29. 两数相除
有点快速幂的思想在里面,主要就是倍增做减法。

class Solution {
public:
    int divide(int x, int y) {
        typedef long long LL;
        vector<LL> exp;//指数项
        bool is_minus = false;//负号
        if (x < 0 && y > 0 || x > 0 && y < 0) is_minus = true;//异号相除的结果为负
        //预处理2*b^i
        LL a = abs((LL)x), b = abs((LL)y);//先直接用正数算,到时候结果加上负号就可以
        for (LL i = b; i <= a; i = i + i) exp.push_back(i);//i从除数开始,只要i还小于被除数a,就倍增i,然后将指数项插入到exp数组里

        LL res = 0;//存答案
        for (int i = exp.size() - 1; i >= 0; i -- )//从大到小开始减
            if (a >= exp[i]) {//还可以做减法
                a -= exp[i];//减去对应的数
                res += 1ll << i;//左移一位,相当于乘以2,因为是基于倍增的思想,所以能减多少次,就左移多少次即可得到结果
            }

        if (is_minus) res = -res;//负号的话就直接取反

        if (res > INT_MAX || res < INT_MIN) res = INT_MAX;//处理溢出的情况

        return res;
    }
};

30. 串联所有单词的子串
困难题,先放着:

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> res;
        if (words.empty()) return res;
        int n = s.size(), m = words.size(), w = words[0].size();
        unordered_map<string, int> tot;
        for (auto& word : words) tot[word] ++ ;

        for (int i = 0; i < w; i ++ ) {
            unordered_map<string, int> wd;
            int cnt = 0;
            for (int j = i; j + w <= n; j += w) {
                if (j >= i + m * w) {
                    auto word = s.substr(j - m * w, w);
                    wd[word] -- ;
                    if (wd[word] < tot[word]) cnt -- ;
                }
                auto word = s.substr(j, w);
                wd[word] ++ ;
                if (wd[word] <= tot[word]) cnt ++ ;
                if (cnt == m) res.push_back(j - (m - 1) * w);
            }
        }

        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值