[LEETCODE]算法进阶自练习15-16 贪心算法&回溯算法

248 阅读2分钟

[LEETCODE]算法进阶自练习15-16 贪心算法&回溯算法

15.贪心算法

简单
  1. 分发饼干 leetcode 455  贪心算法,需要先将孩子的胃口值和饼干的大小进行排序,从小到大,然后尽量将满足一个孩子胃口最小值的饼干分配给孩子即可。
    int findContentChildren(vector<int>& g, vector<int>& s) {
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int gsize = g.size(), ssize = s.size(), j = 0, count = 0;
        while(count<gsize && j<ssize){
            if(s[j] >= g[count]){
                count++;
            }
            j++;
        }
        return count;
    }
中等
  1. 买卖股票的最佳时机含手续费 714  动态规划解法,每次买股票的时候减掉手续费就可以了:
    int maxProfit(vector<int>& prices, int fee) {
        int psize = prices.size();
        if(psize < 2) return 0;
        vector<vector<int>> dp(psize, vector<int>(2,0));
        dp[0][1] = -prices[0];
        for(int i=1; i<psize; i++){
            dp[i][0] = max(dp[i-1][0],dp[i-1][1] + prices[i] - fee);
            dp[i][1] = max(dp[i-1][1],dp[i-1][0] - prices[i]);
        }
        return dp[psize-1][0];
    }

 动态规划压缩空间解法:

    int maxProfit(vector<int>& prices, int fee) {
        int psize = prices.size();
        if(psize < 2) return 0;
        int preCash = 0;
        int preStock = -prices[0];
        for(int i=1; i<psize; i++){
            preCash = max(preCash, preStock + prices[i] - fee);
            preStock = max(preStock, preCash - prices[i]);
        }
        return preCash;
    }

16.回溯算法

简单
  1. 字母大小写全排列 leetcode 784
    vector<char> getLetters(const char c){
        vector<char> ret;
        ret.push_back(c);
        if(c >= 'a' && c <= 'z') ret.push_back(c + 'A'-'a');
        if(c >= 'A' && c <= 'Z') ret.push_back(c + 'a' - 'A');
        return ret;
    }
    vector<string> letterCasePermutation(string S) {
        vector<string> ret;
        int ssize = S.size();
        if(ssize == 0) return ret;

        for(int i=0; i<ssize; i++){
            vector<string> tmp;
            tmp.swap(ret);
            int tsize = tmp.size();
            vector<char> alphas = getLetters(S[i]);
            int asize = alphas.size();

            if(tsize == 0){
                for(int j=0; j<asize; j++){
                    string t(1,alphas[j]);
                    ret.emplace_back(t);
                }
                continue;
            }
            for(int k=0; k<tsize; k++){
                for(int j=0; j<asize; j++){
                    ret.emplace_back(tmp[k] + alphas[j]);
                }
            }
            
        }
        return ret;
    }
中等
  1. 子集 leetcode 78  递归解法:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> ret(1);
        int nsize = nums.size();
        for(int i=0; i<nsize; i++){
            int rsize = ret.size();
            for(int j=0; j<rsize; j++){
                vector<int> t = ret[j];
                ret[j].emplace_back(nums[i]);
                ret.emplace_back(t);
            }
        }
        return ret;
    }

 回溯解法:

class Solution {
public:
    void backtrack(vector<vector<int>>& ret, vector<int>& nums, int start, vector<int>& track){
        ret.push_back(track); // 不继续往下选的情况
        int nsize = nums.size();
        for(int i=start; i<nsize; i++){
            track.push_back(nums[i]); // 继续往下选的情况
            backtrack(ret, nums, i+1, track);
            track.pop_back(); 
        }
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> ret;
        vector<int> track;
        backtrack(ret,nums,0,track);
        return ret;
    }
};

 二进制位映射字典解法:

    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> ret;
        int nsize = nums.size(), mask = 1 << nsize;
        for(int i=0; i<mask; i++){
            vector<int> t;
            for(int j=0; j<nsize; j++){ // 判断每一位上是否为1,为1则将对应的数字push进去
                if(i&(1<<j)) t.push_back(nums[j]);
            }
            ret.push_back(t);
        }
        return ret;
    }
  1. 全排列 leetcode 46  回溯解法:
    void backtrack(vector<vector<int>>& ret, vector<int>& nums, int first, int len){
        if(first == len){       // 得到一个解,直接放入到ret中。
            ret.emplace_back(nums);
            return;
        } 
        int nsize = nums.size();
        for(int i=first; i<nsize; i++){
            swap(nums[i], nums[first]); // 交换第i个和第first个的位置
            backtrack(ret, nums, first+1, len); // 继续递归往后面走
            swap(nums[i], nums[first]); // 将第i个和第first交换回来
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> ret;
        backtrack(ret, nums, 0, (int)nums.size());
        return ret;
    }
  1. 组合 leetcode 77  回溯解法:
    void backtrack(vector<vector<int>>& ret, vector<int>& track, int start, int n, int k){
        if(track.size() == k){
            ret.push_back(track); 
            return;
        }
        for(int i=start; i<=n - (k - track.size()) + 1; i++){
            track.push_back(i);
            backtrack(ret, track, i+1, n, k);
            track.pop_back();
        }
    }
    vector<vector<int>> combine(int n, int k) {
        vector<vector<int>> ret; vector<int> track;
        backtrack(ret, track, 1, n, k);
        return ret;
    }

 字典序解法:

    vector<vector<int>> combine(int n, int k) {
        vector<int> t;
        vector<vector<int>> ret;
        for(int i=1; i<=k; i++) t.push_back(i);
        t.push_back(n+1);
        int j = 0;
        while(j<k){
            ret.emplace_back(t.begin(),t.begin()+k);
            j = 0;
            while(j<k && t[j]+1 == t[j+1]){
                t[j] = j+1;
                ++j;
            }
            ++t[j];
        }
        return ret;
    }
困难
  1. N皇后 leetcode 51  基于集合的回溯解法:
    vector<string> generateBoard(vector<int>& queen, int n){
        vector<string> ret = vector<string>();
        for(int i=0; i<n; i++){
            string row = string(n,'.');
            row[queen[i]] = 'Q';
            ret.emplace_back(row);
        }
        return ret;
    }
    void backtrack(vector<vector<string>>& ret,  vector<int> &queen, int n, int row, unordered_set<int>& col, unordered_set<int>& c1, unordered_set<int>& c2){
        
        if(n == row){
            ret.emplace_back(generateBoard(queen,n));
            return;
        }
        else{
            for(int i=0; i<n; i++){
                int i1 = row-i, i2 = row+i;
                if(col.find(i) != col.end()) continue;
                if(c1.find(i1) != c1.end()) continue;
                if(c2.find(i2) != c2.end()) continue;
                
                queen[row] = i;
                col.insert(i);
                c1.insert(i1);
                c2.insert(i2);
                backtrack(ret, queen, n, row+1, col, c1, c2);
                queen[row] = -1;
                col.erase(i);
                c1.erase(i1);
                c2.erase(i2);
            }
        }
    }
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> ret;
        vector<int> queen(n,-1);
        unordered_set<int> col, c1, c2;
        backtrack(ret, queen, n, 0, col, c1, c2);
        return ret;
    }

 基于位运算的回溯解法:

    vector<string> generateBoard(vector<int>& queen, int n){
        vector<string> ret = vector<string>();
        for(int i=0; i<n; i++){
            string row = string(n,'.');
            row[queen[i]] = 'Q';
            ret.emplace_back(row);
        }
        return ret;
    }
    void solve(vector<vector<string>> &ret, vector<int> &queen, int n, int row, int col, int c1, int c2) {
        if (row == n) {
            ret.emplace_back(generateBoard(queen, n));
        } else {
            int a_pos = ((1 << n) - 1) & (~(col | c1 | c2));
            while (a_pos != 0) {
                int pos = a_pos & (-a_pos);
                a_pos = a_pos & (a_pos - 1);
                int col = __builtin_ctz(pos);
                queen[row] = col;
                solve(ret, queen, n, row + 1, col | pos, (c1 | pos) >> 1, (c2 | pos) << 1);
                queen[row] = -1;
            }
        }
    }
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> ret;
        vector<int> queen(n,-1);
        unordered_set<int> col, c1, c2;
        solve(ret, queen, n, 0, 0, 0, 0);
        return ret;
    }