LeetCode 21 ~ 30

LeetCode 21. 合并两个有序链表

  1. 新建头部的保护结点 dummy,设置 cur 指针指向dummy
  2. 若当前 l1 指针指向的结点的值 vall2指针指向的结点的值val,则令 curnext 指针指向l1,且 l1后移;否则指向 l2,且l2 后移。
  3. 然后cur指针按照上一部设置好的位置后移。
  4. 循环以上步骤直到l1l2 为空。
  5. 将剩余的l1l2接到cur指针后边。
/**
 * 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;
    }
};

LeetCode 22. 括号生成

递归

  1. 左括号的数量不大于 n
  2. 右括号的数量不大于左括号的数量
    符合上述两个条件的括号序列为合法序列,在进行回溯的时候判断序列是否合法即可
class Solution {
public:
    vector<string> res;
    vector<string> generateParenthesis(int n) {
        dfs(n, n, "");
        return res;
    }

    void dfs(int lc, int rc, string path) {
        if(!lc && !rc) res.push_back(path);
        else {
            if(lc < rc) dfs(lc, rc - 1, path + ')');
            if(lc) dfs(lc - 1, rc, path + '(');
        }
    }
};

LeetCode 23. 合并K个排序链表

  1. 一开始先用小根堆存储k个排序链表的头指针,每次操作后用小根堆维护k个链表当前元素最小的指针,并以指针对应的值进行排序
  2. 操作过程中,当小根堆不为空时,堆顶元素即当前k个排序链表当前最小的元素的指针t,将该值加入到dummy链表的后面,并把t指针往后走一位,使得t指针指向的值变大,再加入到小根堆中
/**
 * 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:
	//堆的排序规则
    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);

        while(heap.size()) {
            auto t = heap.top();
            heap.pop();

			//指向下一个节点,后压入堆中
            tail = tail -> next = t;
            if(t -> next ) heap.push(t -> next);
        }
        return dummy -> next;
    }
};

LeetCode 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 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;
    }
};

LeetCode 25. K 个一组翻转链表

  1. 将后k个元素进行翻转操作,共操作k - 1次
  2. 将后k个元素倒序后的序列的尾部指向后k + 1的元素
  3. 将p.next指向倒序后的序列的头部
  4. 更新p指针的位置,即指向倒序后的序列的尾部
    在这里插入图片描述
/**
 * 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* 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; i ++ ) q = q -> next;
            //确保有k个节点
            if(!q) break;
            auto a = p -> next, b = a -> next;
            //k个节点内部反正,执行k - 1次
            for(int i = 0; i < k - 1; i ++ ) {
                auto c = b -> next;
                b -> next = a;
                a = b, b = c;
            }
            //c指针指向当前翻转后的k个节点序列尾部
            auto c = p -> next;
            //c指针的next节点指向原先序列的第k + 1个节点
            p -> next = a, c -> next = b;
            //p指针指向c指针存储的位置
            p = c;
        }
        return dummy -> next;
    }
};

LeetCode 26. 删除排序数组中的重复项

利用指针k记录当前共有几个不重复的元素
初始令 k 为 0,i 从位置 0 开始遍历,若发现 nums[i] 和 nums[i- 1] 不相等,则说明找到新的元素,并且 nums[k ++ ] 赋值为 nums[i],如此循环,直到找到重复元素为止

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

LeetCode 27. 移除元素

思路类比LeetCode 26. 删除排序数组中的重复项
利用指针k记录当前共有几个元素的值不等于val
1.if(nums[i] != val) nums[k ++ ] = nums[i];
2.if(num[i] == val) continue;

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) continue;
            nums[k ++ ] = nums[i];
        }
        return k;
    }
};

LeetCode 28. 实现 strStr()

kmp算法

  1. 创建模式串的 next 数组,为当前失配后,下一个可以比较的位置。
  2. 在待匹配串上应用 next 数组。
class Solution {
public:
    int strStr(string s, string p) {
        if(p.empty()) return 0;
        int n = s.size(), m = p.size();
        s = ' ' + s, p = ' ' + p;

        vector<int> next(m + 1);
        for(int i = 2, j = 0; i <= m; i ++ ) {
            while(j && p[i] != p[j + 1]) j = next[j];
            if(p[i] == p[j + 1]) j ++ ;
            next[i] = j;
        }

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

LeetCode 29. 两数相除

类似于二进制位运算的思想方法
1、先考虑相除后是正数的情况, x y \frac{x}{y} yx = t,因此 x = t × \times ×y,将b,2 × \times × b,4 × \times ×b,8 × \times ×b,…2n × \times ×b的所有小于a的数值存在exp链表中,且exp链表元素大小从小到大排列
2、从exp末端开始枚举,若a >= exp[i],则表示 t 包含1 << i这个数值,将 2i 加入到res中,并且更新a,a -= exp[i]

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;

        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 -- )
            if(a >= exp[i]) {
                a -= exp[i];
                res += 1ll << i;
            }
        
        if(is_minus) res = -res;
        if(res > INT_MAX || res < INT_MIN) res = INT_MAX;
        
        return res;
    }
};

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

  1. 假设 s 的长度为 n,单词的长度为 len,单词的个数为 tot。
  2. 由于数组中每个单词长度都是相同的,故可以按照单词的长度 len 来将 s 划分为 len 种候选单词序列。举例来看 barfoothefoobarman,根据单词长度为 3,可以得到 3 种不同的划分,分别是 bar foo the foo bar man、arf oot hef oob arm an 和 rfo oth efo oba rma n 三种。
  3. 对于以上的每一种划分来说,仅需要用 unordered_map 就可以暴力的算出来,能否能由单词列表中的所有单词拼接而成。
  4. 首先用hash表存储词典中每个单词出现的个数,每个单词长度我们记为len。
  5. 对于每个位置,我们同样使用一个hash表存储从这个位置开始的所有字符串及其出现次数,然后依次把它后面长度为len的字符串拿出来。
  6. 如果这个字符串在hash表中没有出现过,那么返回false;否则判断当前字符串出现个数是否大于目标个数,如果大于,返回false
  7. 如果等于说明找到了一个新的可满足的字符串,更新cnt,如果所有需要的字符串个数都满足了,就记录答案,返回。
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 - 1 <= 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;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值