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);

《leetcode--递归、回溯和分治》

代码:

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:位运算法(循环解法)

位运算基本操作:

《leetcode--递归、回溯和分治》

解题思路:

如果集合内有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] = '.';
            }
        }
    }
};
    原文作者:递归与分治算法
    原文地址: https://blog.csdn.net/xiayto/article/details/78453881
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞