1. 程式人生 > >leetcode--遞迴、回溯和分治

leetcode--遞迴、回溯和分治

基本概念

leetcode題目

78. Subsets

題意:

給出一個數列,求出這個數列所有子集

解題思路:

用遞迴解決,設計思路:
資料結構:
一個vector<int> item儲存集合
一個vector<vector<int>>result 儲存結果
遞迴程式:
generate(i,nums,item,result)
遞迴程式含義:nums[i]前所有元素+nums[i]後所有元素組成的所有子集放入result中
結束條件:i=nums.size()
遞迴過程:

item.push_back(nums[i]);
generate(i+1
,nums,item,result); item.pop(nums[i]); generate(i+1,nums,item,result);

遞迴流程圖

程式碼:

class Solution {
public:
    std::vector<std::vector<int> > subsets(std::vector<int>& nums) {
        std::vector<std::vector<int> > result;
        std::vector<int> item;
        result.push_back(item);//先將空集壓入
generate(0, nums, item, result); return result; } private: void generate(int i, std::vector<int>& nums, std::vector<int> &item, std::vector<std::vector<int> > &result){ if (i >= nums.size()){ return
; } item.push_back(nums[i]); result.push_back(item); generate(i + 1, nums, item, result); item.pop_back(); generate(i + 1, nums, item, result); } };

思路2:位運演算法(迴圈解法)

位運算基本操作:

位運算基本操作

解題思路:

如果集合內有n個數,那麼就有2^n個子集
從0到2^(n-1)迴圈,用二進位制表示,每一個數表示一種集合。
每一個數根據每個位,遍歷n個數是否在所表示的集合內。

程式碼:

public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> result;
        int n=nums.size();
        int all_set=1<<n;
        for(int i=0;i<all_set;i++){
            vector<int> item;
            for(int j=0;j<n;j++){
                if(i&(1<<j)) item.push_back(nums[j]);
            }
            result.push_back(item);
        }
        return result;
    }
};

90. Subsets II

題意:

找子集,要求子集沒有重複。

解題思路:

有兩種重複的子集,一種是元素和順序都相同,一種只有元素相同
1:[1,2,2]和[1,2,2]
2:[1,2,2]和[2,1,2]
對於第一種重複,可以用set去除
對於第二種重複,可以先對元素進行排序,使它只剩下第一種重複

程式碼:

class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> result;
        set<vector<int>> res_set;
        vector<int> item;
        result.push_back(item);
        generate(0,nums,item,res_set,result);
        return result;
    }
private:
    void generate(int i,vector<int>& nums,vector<int> &item,set<vector<int>> &res_set,vector<vector<int>> &result){
        if(i==nums.size()) return;
        item.push_back(nums[i]);
        if(res_set.find(item)==res_set.end()){
            res_set.insert(item);
            result.push_back(item);
        }
        generate(i+1,nums,item,res_set,result);
        item.pop_back();
        generate(i+1,nums,item,res_set,result);
    }
};

40. Combination Sum II

題意:

找到相加之為和為T的子集。

解題思路

運用減枝的思想,縮減遞迴運算的時間複雜度,就是已經不滿足的條件的分枝不再進行遞迴。

程式碼:

class Solution {
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<int> item;
        vector<vector<int>> result;
        set<vector<int>> res_set;
        sort(candidates.begin(),candidates.end());
        generate(0,candidates,item,result,res_set,0,target);
        return result;
    }
private:
    void generate(int i,vector<int> &candidates, vector<int> &item,vector<vector<int>> &result,set<vector<int>> &res_set,int sum,int target){
        if(i>=candidates.size()||sum>target) return;
        item.push_back(candidates[i]);
        sum+=candidates[i];
        if(res_set.find(item)==res_set.end()&&sum==target){
            res_set.insert(item);
            result.push_back(item);
        }
        generate(i+1,candidates,item,result,res_set,sum,target);
        item.pop_back();
        sum-=candidates[i];
        generate(i+1,candidates,item,result,res_set,sum,target);
    }

};

22. Generate Parentheses

題意:

輸入括號數n,輸出所有合法的括號組合的集合。

解題思路:

用遞迴的方法解:
每次加一個 ‘(’ 或者‘ ) ’,加‘)’的條件是‘(’的數目比較多。
遞迴結束條件:當字串長度達到2n
用left記錄左擴號剩餘數目
用right記錄右括號剩餘數目
每次遞迴left-1或者right-1,用條件過濾掉不滿足條件的項。

程式碼:

class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<string> result;
        generate("",n,n,result);
        return result;
    }
private:
    void generate(string item,int left,int right,vector<string> &result){
        if(left==0&&right==0) {
            result.push_back(item);
            return;
        }
        if(left>0){
            generate(item+'(',left-1,right,result);
        }
        if(left<right){
            generate(item+')',left,right-1,result);
        }
    }
};

51. N-Queens

題意:

在國際象棋n*n的棋盤上放置n個皇后,使它們互相不在攻擊範圍之內。

解題基礎:

方向陣列:
每放下一個皇后,它的上下左右和兩個對角線,一共8個方向都不能放置,用一個二維陣列記錄能放置的位置為0,不能的為1。如果僅僅用迴圈,程式碼比較複雜,應該用方向陣列:

static const int dx[] = {-1, 1, 0, 0, -1, -1, 1, 1};
static const int dy[] = {0, 0, -1, 1, -1, 1, -1, 1};

dx[i],dy[i]表示一個方向,用兩個迴圈,一個迴圈遍歷所有的方向,一個迴圈遍歷方向上所有的數。
static的意思是靜態分配記憶體,就是對於經常使用的相同的常量,只分配一次空間,不用每次呼叫都分配一次記憶體。

void put_down_the_queen(int x, int y,std::vector<std::vector<int> > &mark){
        static const int dx[] = {-1, 1, 0, 0, -1, -1, 1, 1};
        static const int dy[] = {0, 0, -1, 1, -1, 1, -1, 1};
        mark[x][y] = 1;
        for (int i = 1; i < mark.size(); i++){
            for (int j = 0; j < 8; j++){
                int new_x = x + i * dx[j];
                int new_y = y + i * dy[j];
                if (new_x >= 0 && new_x < mark.size() &&
                    new_y >= 0 && new_y < mark.size()){
                    mark[new_x][new_y] = 1;
                }
            }
        }
    }

解題思路:

資料結構:
location儲存result的圖
mark 儲存可以放的位置
步驟:
1 先初始化各個資料結構。
2 寫好放皇后的方法,放入皇后同時改變location和mark陣列
3 每次在第k行找一個位置放皇后,遍歷第k行的所有列,遞迴呼叫地找第k+1行中的皇后。
遞迴結束條件:k==n;
遞迴過程:
generate(k, location, mark,result)
用一個迴圈遍歷k行中所有列,若有為0的列,遞迴呼叫generate
遞迴前需要設定mark陣列,所以要先用臨時mark儲存mark,遞迴呼叫後還原mark,使每次迴圈的mark不變。

程式碼:

class Solution {
public:
    std::vector<std::vector<std::string> > solveNQueens(int n) {
        std::vector<std::vector<std::string> > result;
        std::vector<std::vector<int> > mark;
        std::vector<std::string> location;
        for (int i = 0; i < n; i++){
            mark.push_back((std::vector<int>()));
            for (int j = 0; j < n; j++){
                mark[i].push_back(0);
            }
            location.push_back("");
            location[i].append(n, '.');
        }
        generate(0, n, location, result, mark);
        return result;
    }
private:
    void put_down_the_queen(int x, int y,
                            std::vector<std::vector<int> > &mark){
        static const int dx[] = {-1, 1, 0, 0, -1, -1, 1, 1};
        static const int dy[] = {0, 0, -1, 1, -1, 1, -1, 1};
        mark[x][y] = 1;
        for (int i = 1; i < mark.size(); i++){
            for (int j = 0; j < 8; j++){
                int new_x = x + i * dx[j];
                int new_y = y + i * dy[j];
                if (new_x >= 0 && new_x < mark.size() &&
                    new_y >= 0 && new_y < mark.size()){
                    mark[new_x][new_y] = 1;
                }
            }
        }
    }
    void generate(int k, int n,
                std::vector<std::string> &location,
                std::vector<std::vector<std::string> > &result,
                std::vector<std::vector<int> > &mark){
        if (k == n){
            result.push_back(location);
            return;
        }
        for (int i = 0; i < n; i++){
            if (mark[k][i] == 0){
                std::vector<std::vector<int> > tmp_mark = mark;
                location[k][i] = 'Q';
                put_down_the_queen(k, i, mark);
                generate(k + 1, n, location, result, mark);
                mark = tmp_mark;
                location[k][i] = '.';
            }
        }
    }
};