Leetcode每日一题专题

通配符匹配

字符串、动态规划

思路:

动态规划法:

1.定义数组dp[i][j]:s的前i个字符串匹配p的前j个字符串

2.数组之间的关系:假设p[j] = '*' 那么如果使用星号就等于dp[i - 1][j],如果不使用就等于dp[i][j - 1],假设为问号或者s和p匹配,那么就等于dp[i - 1][j - 1]

3.初始化数组:dp[0][0] = true,并且当p = '*'且s为空时,dp[0][i] = true

字符串匹配法:

设定两个标识位,分别标识当存在星号时,i 和 j 的位置。逻辑是假设为问号或者s和p匹配,那么就i++、j++,当是星号时,从匹配空遍历后面的s字符串,当发现不匹配时,就返回标识位继续遍历,直到最后。

代码:

动态规划法:

class Solution {
public:
    bool isMatch(string s, string p) {
        int n = s.size() , m = p.size();
        vector<vector<bool>> dp(n + 1 , vector<bool>(m + 1));
        dp[0][0] = true;
        for(int i = 1; i <= m; i++) {
            if(p[i - 1] == '*')     dp[0][i] = true;
            else    break;
        }
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++) {
                if(p[j - 1] == '*') {
                    dp[i][j] = dp[i][j - 1] | dp[i - 1][j];
                } else if(p[j - 1] == '?' || p[j - 1] == s[i - 1]) {
                    dp[i][j] = dp[i - 1][j - 1];
                } 
            }
        }
        return dp[n][m];
    }
};

字符串匹配法:

class Solution {
public:
    bool isMatch(string s, string p) {
        int i = 0 , j = 0 , iStar = -1 , jStar = -1 , n = s.size() , m = p.size();
        while(i < n) {
            if(p[j] == '?' || s[i] == p[j]) {
                i++; j++;
            } else if(j < m && p[j] == '*') {
                jStar = j++;
                iStar = i;
            } else if(iStar >= 0) {
                i = ++iStar;
                j = jStar + 1;
            } else      return false;
        }
        while(j < m && p[j] == '*')     j++;
        return j == m;
    }
};

总结

  1. 动态规划数组中,i 和 j 分别表示的是s和p匹配的字符数量,所以在匹配字符串时记得要 -1 
  2. 字符串法中,当匹配完成后,如果p的结尾存在多余的星号时记得“删除”

347. 前 K 个高频元素

解题思路:

使用哈希表存储出现次数数组,优先队列来找到前k个出现频率最高的数。优先队列的排序方式以count值从堆顶到堆尾从小到大排序。

桶排序

这也是最先能想到的思路,使用哈希表存储出现次数数组,使用二维数组从小到大存储次数和数据。然后除前k个值即可。

快速排序法:

不需要每个都排序,随机选取一个数,以此为中间值,分为左边和右边,若k小于左边的数组数量,说明k在左子数组,再对左边数组进行分割。若k大于左边的数组数量,证明前k个数必须都包括左子数组,剩下的对右子数组在进行分割。

代码:

class Solution {
public:
    static bool cmp(pair<int, int>& m, pair<int, int>& n) {
        return m.second > n.second;
    }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int , int> mii;
        for(int i = 0; i < nums.size(); i++) {
            mii[nums[i]]++;
        }
        priority_queue<pair<int , int> , vector<pair<int , int>> , decltype(&cmp)> q(cmp);
        for(auto& [num , count] : mii) {
            if(q.size() == k) {
                if(q.top().second < count) {
                    q.pop();
                    q.emplace(num , count);
                }
            } else {
                q.emplace(num , count);
            }
        }
        vector<int> res;
        while(!q.empty()) {
            res.push_back(q.top().first);
            q.pop();
        }
        return res;
    }
};

桶排序

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        map<int , int> mii;
        for(int i = 0; i < nums.size(); i++) {
            mii[nums[i]]++;
        }
        vector<vector<int>> ivec(nums.size() + 1);
        vector<int> res(k);
        int count = 0;
        for(auto it = mii.begin(); it != mii.end(); it++) {
            ivec.at(it -> second).push_back(it -> first);
        }
        for(int i = ivec.size() - 1; i >= 0; i--) {
            for(int j = 0; j < ivec[i].size(); j++) {
                res.at(count) = ivec.at(i).at(j);
                count++;
                if(count == k)      return res;
            }
        }
        return res;
    }
};

快速排序法:

class Solution {
public:
    vector<int> res;

    void qsort(vector<pair<int , int>>& v , int start , int end , int k) {
        int picked = rand() % (end - start + 1) + start;
        swap(v[start] , v[picked]);

        int num = v[start].second;
        int index = start;
        for(int i = start + 1; i <= end; i++) {
            if(v[i].second >= num) {
                swap(v[i] , v[++index]);
            }
        }
        swap(v[start] , v[index]);
        if(k <= index - start) {
            qsort(v , start , index - 1 , k);
        } else {
            for(int i = start; i <= index; i++) {
                res.push_back(v[i].first);
            }
            if(k > index - start + 1) {
                qsort(v , index + 1 ,  end , k - (index - start + 1));
            }
        }
    }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int , int> mii;
        for(int i = 0; i < nums.size(); i++) {
            mii[nums[i]]++;
        }
        vector<pair<int , int>> values;
        for(auto& kv : mii) {
            values.push_back(kv);
        }
        qsort(values , 0 , values.size() - 1 , k);
        return res;
    }
};

 

总结:

  1. 如何使用自定义排序方法来使用优先队列的排序方式
  2. 如何使用优先队列

桶排序

  1. 如何巧妙的对次数进行排序。我们先创建一个nums.size() + 1的二维数组,然后根据次数作为索引进行数据的存储,这样的数组就是从小到大排序了
  2. 因为索引值从0开始,要根据出现次数进行排序,我们要创建nums.size() + 1的数组。不然会越界

快速排序法

  1. 为了避免最坏情况的出现,即每次取的中枢数组的元素都位于数组的两端,我们使用随机抽取中位数的方法
  2. 如何正确的进行自定义的快速排序。
    1. 以num为中位数,num为nums[picked]
    2. index为除开start以外,符合左右子数组规律的下标
    3. 最后进行start和index的交换,就可以完成快速排序
  3. 只有k > index - start + 1的时候才进行右子数组的快速排序
  4. end值是value.size() - 1,不是nums.size() - 1

扩展:

TOPK问题:

TOP-K问题是面试中的常见题型,具体表现为:

  1. 海量数据
  2. 求最大(最小)的K个值.

具体实现的方法主要有2种:

  1. 堆方法
  2. 快速排序法

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值