• 回溯-递归练习题集


    给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

    说明:解集不能包含重复的子集。

    示例:

    输入: nums = [1,2,3]
    输出:
    [
    [3],
      [1],
      [2],
      [1,2,3],
      [1,3],
      [2,3],
      [1,2],
      []
    ]

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/subsets
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    思路分析:

    1:使用递归和回溯的思想解决

    对nums从0到结束,分别把当前第nums[i]个元素放入item和不放入item,把当前item存为结果后分别递归该函数,求得子集。

        void generate(int i,vector<int> &nums,vector<int> &item,vector<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);
        }
    class Solution {
    public:
    
        vector<vector<int> > subsets(vector<int>& nums) {
            vector<vector<int> > result;
            vector<int>item;
            result.push_back(item);
            generate(0,nums,item,result);
            return result;
        }
    
        
    };

    2:利用位运算和二进制保存结果

    比如对集合ABCD求他的子集 把ABCD看成4位二进制数,每一位分别对应ABCD四个数,求其子集就是求所有4位二进制数所对应的集合。

    class Solution {
    public:
        vector<vector<int> > subsets(vector<int>& nums) {
            vector<vector<int> > result;
            int all_set = 1 << nums.size();//2^n
            for(int i = 0; i < all_set; i++)
            {
                vector<int> item;
                for(int j = 0; j < nums.size(); j++)
                {
                    if(i & (1 << j) )//1<<j代表当前nums数组的第j个元素如果和i相与为真就把它加入集合
                    {
                        item.push_back(nums[j]);
                    }
                }
                result.push_back(item);
    
            }
            return result;
        }
    
    };

    90. 子集 II

    给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

    说明:解集不能包含重复的子集。

    示例:

    输入: [1,2,2]
    输出:
    [
    [2],
    [1],
    [1,2,2],
    [2,2],
    [1,2],
    []
    ]

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/subsets-ii
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    主要思路:这个题和上一个题类似 主要思路是先对数组进行排序,之后再递归所有子集的时候利用set的find功能去重。

    void Generate(int i,vector<int> &nums, vector<int> &item,vector<vector<int> >&result,set<vector<int> > &reset)
    {
        if(i>= nums.size())
            return;
    
        item.push_back(nums[i]);
        if(reset.find(item) == reset.end())//没有找见
        {
            result.push_back(item);
            reset.insert(item);
        }
        Generate(i+1,nums,item,result,reset);
        item.pop_back();
        Generate(i+1,nums,item,result,reset);
    }
    class Solution {
    public:
        vector<vector<int> > subsetsWithDup(vector<int>& nums) {
            sort(nums.begin(),nums.end());
            vector<vector <int> > result;
            vector<int> item;
            set<vector<int> > reset;
            result.push_back(item);
            Generate(0,nums,item,result,reset);
            return result;
        }
    };

    40. 组合总和 II

    给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

    candidates 中的每个数字在每个组合中只能使用一次。

    说明:

    所有数字(包括目标数)都是正整数。
    解集不能包含重复的组合。 
    示例 1:

    输入: candidates = [10,1,2,7,6,1,5], target = 8,
    所求解集为:
    [
    [1, 7],
    [1, 2, 5],
    [2, 6],
    [1, 1, 6]
    ]
    示例 2:

    输入: candidates = [2,5,2,1,2], target = 5,
    所求解集为:
    [
      [1,2,2],
      [5]
    ]

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/combination-sum-ii
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

     其实这个题也可以用位运算 不过输入太大 超时或超出限制……

    class Solution {
    public:
        vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<vector<int> > result;
        set<vector<int> > reset;
        sort(candidates.begin(),candidates.end());
        int all_set = 1 << candidates.size();//2^n
        for(int i = 0; i < all_set; i++)
            {
                vector<int> item;
                int sum = 0;
                for(int j = 0; j < candidates.size(); j++)
                {
                    if(i & (1 << j) )//1<<j代表当前nums数组的第j个元素如果和i相与为真就把它加入集合
                    {
                        item.push_back(candidates[j]);
                        sum += candidates[j];
                    }
                }
                if((sum == target) && (reset.find(item)) == reset.end())
                result.push_back(item);
                reset.insert(item);
    
            }
            return result;
        }
    };
    View Code

    还是看看有没有好方法,剪枝!比如选择的数字本身已经超过了target 就可以结束递归。同理 如果sum和超过了target也可以剪枝。

    void Generate(int sum,int target,int i,vector<int> &nums, vector<int> &item,vector<vector<int> >&result,set<vector<int> > &reset)
    {
        if(i>= nums.size())
            return;
        if(nums[i] > target)
        return;
        if(sum > target)
        return;
        item.push_back(nums[i]);
        sum+=nums[i];
        if(sum == target && reset.find(item) == reset.end())//没有找见
        {
            result.push_back(item);
            reset.insert(item);
            
        }
        Generate(sum,target,i+1,nums,item,result,reset);
        sum-=nums[i];
        item.pop_back();
        Generate(sum,target,i+1,nums,item,result,reset);
    }
    class Solution {
    public:
        vector<vector<int>> combinationSum2(vector<int>& nums, int target) {
            sort(nums.begin(),nums.end());
            vector<vector <int> > result;
            vector<int> item;
            set<vector<int> > reset;
            Generate(0,target,0,nums,item,result,reset);
            return result;
        }
    };

    22. 括号生成

    给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。

    例如,给出 n = 3,生成结果为:

    [
    "((()))",
    "(()())",
    "(())()",
    "()(())",
    "()()()"
    ]

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/generate-parentheses
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

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

    51. N皇后

    n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

     

    上图为 8 皇后问题的一种解法。

    给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。

    每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

    示例:

    输入: 4
    输出: [
    [".Q..", // 解法 1
    "...Q",
    "Q...",
    "..Q."],

    ["..Q.", // 解法 2
    "Q...",
    "...Q",
    ".Q.."]
    ]
    解释: 4 皇后问题存在两个不同的解法。

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/n-queens
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    void put_down(int x,int y,vector<vector<int> > &mark)
    {
        static int dx[] = {-1,0,1,-1,1,-1,0,1};
        static int dy[] = {-1,-1,-1,0,0,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 < mark.size() && new_Y < mark.size() && new_X >= 0 && new_Y >= 0)
                {
                    mark[new_X][new_Y] = 1;
                }
            }
    
        }
    }
    
    void Generate(int k, int n,vector<string> &location,vector<vector<string> >&result,vector<vector<int> >&mark)//正在放置第K个皇后
    {
        if(n == k)//棋子放完的时候结束递归
        {
            result.push_back(location);
            return;
        }
        for(int i=0 ;i < n; i++)
        {
            if(mark[k][i] == 0)
            {
                vector<vector<int> > remark = mark;//未放置前的结果
                location[k][i] = 'Q';//放置后的结果
                put_down(k,i,mark);
                Generate(k + 1,n,location,result,mark);//递归放置
                mark = remark;//回溯回退
                location[k][i] ='.';
            }
        }
    }
    
    class Solution {
    public:
        vector<vector<string> > solveNQueens(int n) {
        vector<vector<string> > result;
        vector<string> location;
        vector<vector<int> > mark;
        for(int i = 0; i < n; i++)
        {
            mark.push_back(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;
        }
    };
  • 相关阅读:
    etcd客户端c#
    【Python 2 到 3 系列】 关于除法的余数
    彻底搞定C指针--“函数名与函数指针”
    关于 函数指针和函数名 例子的疑难解答
    stat.h头文件,轻松获取文件属性
    C++指针之间的赋值与转换规则总结
    (转)mblog解读(二)
    (转)mblog解读(一)
    (转)renren-fast解读(二)
    (转)renren-fast解读(一)
  • 原文地址:https://www.cnblogs.com/KID-XiaoYuan/p/12288284.html
Copyright © 2020-2023  润新知