leetcode 21~30

lc 21~30

21. 合并两个有序链表

单链表

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        auto dummy = new ListNode(-1), head = dummy;
        while(list1 || list2){
            int x = list1 != nullptr? list1->val : INT_MAX;
            int y = list2 != nullptr? list2->val : INT_MAX;
            if(x <= y)
                head = head->next = list1, list1 = list1->next;
            else
                head = head->next = list2, list2 = list2->next;
            //只要有哪个数为INF就说明其中一个链表已经全部加入了
            //因此刚才直接连接上剩下的一个链表,就可以结束了
            if(x == INT_MAX || y == INT_MAX)    break;
        }
        return dummy->next;
    }
};

22. 括号生成

DFS

class Solution {
public:
    vector<string> ans;
    vector<string> generateParenthesis(int n) {
        //首先明确有效括号性质:任意前缀左括号数目大于等于右括号数目
        //最终的字符串左括号数等于右括号数都为n
        dfs("", 0, 0, n);
        return ans;
    }

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

23. 合并K个升序链表

1.暴力遍历就完事了,584ms,O(n * k),n为总节点数,k为链表个数

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        auto dummy = new ListNode(-1), head = dummy;
        int len = lists.size();
        while(1){
            int minValm = INT_MAX, seq = 0;//记录最小的节点值,序号
            for(int i = 0; i < len; i++){
                if(lists[i] && lists[i]->val < minValm){
                    minValm = lists[i]->val;
                    seq = i;
                }
            }
            //说明所以链表都遍历完了
            if(minValm == INT_MAX) break;
            head = head->next = lists[seq];
            lists[seq] = lists[seq]->next;
        }
        return dummy->next;
    }
};

2.优先队列, O(n * logk)

class Solution {
public:
    struct Cmp{
        //优先队列与sort排序是相反的
        bool operator()(ListNode* x, ListNode* y){
            return x->val > y ->val;//升序
        }
    };

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        auto dummy = new ListNode(-1), tail = dummy;
        //优先队列,底层为堆,会自动将里面的元素按规则排序,这里堆顶永远为最小值
        priority_queue<ListNode*, vector<ListNode*>, Cmp> heap;
        for(auto node : lists)
            if(node) heap.push(node);
        while(heap.size()){
            auto temp = heap.top();
            heap.pop();
            tail = tail->next = temp;
            if(temp->next) heap.push(temp->next);
        }
        return dummy->next;
    }
};

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

单链表,三点记录更新,迭代

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        //长度0或1直接返回就行
        if(!head || !head->next) return head;
        auto dummy = new ListNode(-1);//虚拟头节点大法,因为交换后head就不是新的头节点了
        dummy->next = head;
        auto pre = dummy, cur = head, ne = head->next;
        while(cur && ne){
            cur->next = ne->next;
            ne->next = cur;
            pre->next = ne;
            pre = cur;
            cur = cur->next;
            //如果新的cur节点非空,才能更新ne
            if(cur) ne = cur->next;
        }
        return dummy->next;
    }
};

y总的代码

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        auto dummy = new ListNode(-1);
        dummy->next = head;
        for(auto p = dummy; p->next && p->next->next;){
            auto a = p->next, b = a->next;
            p->next = b;
            a->next = b->next;
            b->next = a;
            p = a;
        }
        return dummy->next;
    }
};

25. K 个一组翻转链表

单链表,k反转

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        auto dummy = new ListNode(-1);
        dummy->next = head;
        for(auto p = dummy;;){
            auto pp = p;
            //检查p后面是否有k个节点
            for(int i = 0; i < k && pp; i++) pp = pp->next;
            if(!pp) break;//没有k个
            //先将后面的k个节点内部进行指向反转(k-1次)
            auto a = p->next, b = a->next;//双指针
            for(int j = 0; j < k - 1; j++){
                auto c = b->next;
                b->next = a;
                a = b, b = c;
            }
            pp = p->next;
            pp->next = b;
            p->next = a;
            p = pp;
        }
        return dummy->next;
    }
};

26. 删除有序数组中的重复

双指针

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int len = nums.size();
        int j = 0;
        for(int i = 0; i < len; i++){
            if(!i || nums[i] != nums[i - 1])
                nums[j++] = nums[i];
        }
        return j;
    }
};

27. 移除元素

双指针,简单

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

28. 实现 strStr()

KMP模板

class Solution {
public:
    int strStr(string haystack, string needle) {
        //如果s的长度小于p,必不可能匹配上(如果没有这个,当needle空时就出错了)
        //现在力扣长度都改为1~10^4,这样就不会出现y总做的时候那个情况了
        if(haystack.length() < needle.length()) return -1;
        int len1 = haystack.length(), len2 = needle.length();
        string s = ' ' + haystack, p = ' ' + needle;
        vector<int> ne(len2 + 1);//c++普通数组不让用非常数的值创建大小
        //求next
        next(ne, p);
        //匹配,从1开始
        for(int i = 1, j = 0; i <= len1; i++){
            while(j && s[i] != p[j + 1]) j = ne[j];
            if(s[i] == p[j + 1]) j++;
            if(j == len2) return i - j;
        }
        return -1;
    }

    void next(vector<int>& ne, string p){
        int len = p.length() - 1, j = 0;
        //从2开始,因为j + 1与 i匹配,所以i从2开始
        for(int i = 2; i <= len; i++){
            while(j && p[i] != p[j + 1]) j = ne[j];
            if(p[i] == p[j + 1]) j++;
            ne[i] = j;
        }
    }
};

29. 两数相除

二进制位

class Solution {
public:
    int divide(int dividend, int divisor) {
        //二进制位思想,要注意溢出,(-2^31 / -1 = 2^31 溢出)
        //将两个数都取下绝对值,并记录商的符号
        typedef long long LL;
        bool is_minus = false;
        if((dividend < 0 && divisor > 0) || (dividend > 0 && divisor < 0))
            is_minus = true;
        //转LL,防止上面那样溢出
        LL a = abs((LL)dividend), b = abs((LL)divisor), res = 0;
        vector<LL> exp;
        for(LL c = b; c <= a; c += c) exp.push_back(c);
        for(int i = exp.size() - 1; i >= 0; i--){
            if(a >= exp[i]){
                a -= exp[i];
                //这里一定要让1是LL,左移31位就会造成溢出int,如 -2147483648 -1
                res += 1LL << i;
            }
        }
        if(is_minus) res = -res;
        if(res > INT_MAX || res < INT_MIN)  return INT_MAX;
        return res;
    }
};

30. 串联所有单词的子串

哈希表 + 滑动窗口

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ans;
        if(words.empty()) return ans; //不过看数据范围,没有空的,所以不需要
        //很难,终于弄懂了,分组一段一段的匹配,哈希表,cnt记录滑动窗口有效的子串个数
        int len = s.length(), n = words.size(), w = words[0].length(), cnt = 0;
        unordered_map<string, int> tot; //维护一个总的哈希表
        for(auto& s : words) tot[s]++;
        //将偏移量i从0~w-1分为w组,维护一个滑动窗口,j左边n*w长度才为窗口内容(也就是j不包含)
        for(int i = 0; i < w; i++){
            cnt = 0;//每走完一层就将cnt清零,麻了找了好久的错
            unordered_map<string, int> window;//存放窗口内的子串出现次数
            for(int j = i; j + w <= len; j += w){
                //左边出窗口一个单词
                if(j >= i + n * w){//此时左边才至少有m*w长度
                    auto word = s.substr(j - n * w, w);
                    if(tot[word] >= window[word]) cnt--;//说明是有效的子串,出表,cnt--
                    window[word]--;
                }
                //右边入窗口一个单词
                auto word = s.substr(j, w);
                window[word]++;
                if(window[word] <= tot[word]) cnt++;//说明是有效子串++
                if(cnt == n) ans.push_back(j - (n -  1) * w);
                //为什么是n-1,是因为右边窗口已经进入,但j还没更新
            }
        }
        return ans;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值