分门别类刷leetcode——递归和回溯搜索(C++实现)

目录

基础复习

回溯法

leetcode 78 子集

leetcode 90 子集 II

leetcode 40 组合总和 II

leetcode 22 括号生成

leetcode 51 N皇后

leetcode 473 火柴拼正方形


回家了,复习不能停!!!加油!!!    

 

基础复习

 

回溯法

 

leetcode 78 子集

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

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

示例:

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

思路:

方法一——回溯法

 

class Solution {
public:
    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);
        //放i的结果
        generate(i+1,nums,item,result);
        item.pop_back();
        //不放i的结果
        generate(i+1, nums, item, result);
    }
    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;
    }
};

 

 

方法二——位运算

每个元素都有放和不放两种决策。因此一共八种情况。针对每一种情况,依次判断每个元素是否在该集合中出现。

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>>result;
        int all_set=1<<nums.size(); //1<<n 的值为 2的n次幂
        
        for(int i=0; i<all_set; i++){ //i代表集合中的各个情况
            vector<int>item;
            //A  100  即 1<<2, B  010 即 1<<1, C 001 即 1<<0 
            for(int j=0; j<nums.size(); j++){
                if(i&(1<<j)){ //判断当前元素是否在i集合中出现
                    item.push_back(nums[j]);
                }
            }
            result.push_back(item);
        }        
        return result;
    }
};

 

 

 

leetcode 90 子集 II

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

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

示例:

输入: [1,2,2]
输出:
[ [2], [1], [1,2,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;
        int all_set=1<<nums.size(); //1<<n 的值为 2的n次幂
        
        for(int i=0; i<all_set; i++){ //i代表集合中的各个情况
            vector<int>item;
            //A  100  即 1<<2, B  010 即 1<<1, C 001 即 1<<0 
            for(int j=0; j<nums.size(); j++){
                if(i&(1<<j)){ //判断当前元素是否在i集合中出现
                    item.push_back(nums[j]);
                }
            }
            result.push_back(item);
        }        
        
        //利用set去重
        set<vector<int>>con;
        for(auto i : result){
            con.insert(i);
        }
        
        result.clear();
        for(auto i :con){
           result.push_back(i);
        }
        
        return result;
            
    }
};

 

 

 

leetcode 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] ]

思路:

将数组排序,然后分别考虑每一个元素是否加入到集合,每个元素都有加入集合和不加入集合这两种可能。用一个变量sum来累计集合中的各个元素的总和。使用set来进行去重。

class Solution {
public:
    void generate(int i, vector<int>&nums, vector<vector<int>>&result, vector<int>&item, 
                  set<vector<int>>&res_set, int sum, int target){
        if(i>=nums.size()||sum>target) return;//剪枝
        
        //sum为当前自己item中的元素和
        sum+=nums[i];
        item.push_back(nums[i]);
        if(target==sum && res_set.find(item)==res_set.end()){
            result.push_back(item);
            res_set.insert(item);
        }
        //放i且sum小于target的时候
        generate(i+1, nums, result, item, res_set, sum, target);
        sum-=nums[i];
        item.pop_back();
        //不i且sum小于target的时候
        generate(i+1, nums, result, item, res_set, sum, target);      
    }
    
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end());
        set<vector<int>>res_set;
        vector<int>item;
        vector<vector<int>>result;
        generate(0, candidates, result, item, res_set, 0, target);
        return result;
    }
};

 

 

 

leetcode 22 括号生成

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

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

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

思路:

n组括号,字符串长度为2的n次幂,字符串中每个字符都有两种选择,“(”或者“)”。因此有2*(2的n次幂种)  种可能。

放置的时候,如果当前右括号的数量超过左括号,则该组合不合法。

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){
            generate(item+'(',left-1, right, result);
        }
        //保证括号的合法性
        if(left<right){
            generate(item+')', left, right-1, result);
        }
    }
};

 

 

 

leetcode 51 N皇后

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

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

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

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

示例:

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

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

思路:

四个皇后的回溯摆放过程:

   

class Solution {
public:
    void put_down_the_queen(int x, int y, vector<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;//皇后的位置(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;
                }
            }
        }
    }
    
    //参数k代表第k个皇后,某次的存出结果存入location中,最终结果存入result
    void generate(int k, int n, vector<string>&location, 
                  vector<vector<string>>&result, vector<vector<int>>&mark){
        //完成了所有皇后的放置任务
        if(k==n){
            result.push_back(location);
            return;
        }
        //按顺序尝试第0到第n-1列
        for(int i=0; i<n; i++){
            //说明此位置可以放皇后
            if(!mark[k][i]){
                //记录回溯前的mark镜像
                vector<vector<int>>temp_mark=mark;
                //记录当前皇后位置
                location[k][i]='Q';
                //把八个方向做出标记,以后这些位置就不能放皇后了
                put_down_the_queen(k,i,mark);
                //考虑第k+1个皇后的放置方式
                generate(k+1, n, location, result, mark);
                //说明下一行放置不成功,将mark重置为回溯前的状态
                mark=temp_mark;
                //该位置不能放皇后,改为 .
                location[k][i]='.';
            }
        }
    }
    
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>>result;
        vector<vector<int>>mark;
        vector<string>location;
        //初始化一个 n*n的 .  表格
        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;                                
    }
};

 

 

 

leetcode 473 火柴拼正方形

还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。

输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。

示例 1:

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

解释: 能拼成一个边长为2的正方形,每边两根火柴。

示例 2:

输入: [3,3,3,3,4]
输出: false

解释: 不能用所有火柴拼成一个正方形。

注意:

  1. 给定的火柴长度和在 0 到 10^9之间。
  2. 火柴数组的长度不超过15。

方法一(回溯):

如果传入的数组中的元素个数小于4或者数组总和并不能被4整除,则说明数组拼不出正方形。

调用递归函数,以数组总和的1/4作为目标值,回溯尝试每一个元素。

每一根火柴也有两种情况,放入该边上或者不放入该边上。

class Solution {
public:
    bool generate(int i, vector<int>&nums, int target, int bucket[]){
        //说明数组中的元素考察完毕
        if(i>=nums.size()){
            return bucket[0]==target && bucket[1]==target 
                && bucket[2]==target && bucket[3]==target;
        }
        
        for(int j=0; j<4; j++){
            //说明该组合超过了正方形的边长
            if(bucket[j]+nums[i]>target) continue;
            bucket[j]+=nums[i];
            //此次递归的结果返回值为true
            if(generate(i+1, nums, target, bucket)){
                return true;
            }
            //此次递归的结果返回值为false,将最后一个元素的值减去,考虑下一个元素的值
            bucket[j]-=nums[i];
        }
        return false;        
    }
    
    bool makesquare(vector<int>& nums) {
        if(nums.size()<4) return false;
        int sum=0;
        for(auto i:nums){
            sum+=i;
        }
        if(sum%4!=0) return false;
        
        //从大到小排序
        sort(nums.rbegin(), nums.rend());
        
        int bucket[4]={0};
        return generate(0, nums, sum/4, bucket);
    }
};

 

方法二(位运算):

class Solution {
public:
    bool makesquare(vector<int>& nums) {
        if(nums.size()<4) return false;
        
        int sum=0;
        for(auto i:nums)
            sum+=i;
        if(sum%4) return false;
        
        int target=sum/4;
        vector<int>ok_subset;
        vector<int>ok_half;
        int all=1<<nums.size();//有2的nums.size()种可能性
        
        //遍历所有组合情况,找出满足一条边的所有组合
        for(int i=0; i<all; i++){
            int sum=0;
            for(int j=0; j<nums.size(); j++){
                //如果i情况下与j元素没有交集
                if(i&(1<<j)){
                    sum+=nums[j];
                }
            }
            if(sum==target){
                ok_subset.push_back(i);
            }
        }
        
        //遍历所有一条边的情况,找出所有两条边没有交集的组合
        for(int i=0; i<ok_subset.size(); i++){
            for(int j=i+1; j<ok_subset.size(); j++){
                if((ok_subset[i] & ok_subset[j])==0){
                    ok_half.push_back(ok_subset[i] | ok_subset[j]);
                }
            }
        }
        
        //遍历所有的两条边没交集的情况,找出四条边没交集的组合,即为我们要找的结果
        for(int i=0; i<ok_half.size(); i++){
            for(int j=0; j<ok_half.size(); j++){
                //求 & 为 0 ,说明没有交集
                if((ok_half[i] & ok_half[j])==0){
                    return true;
                }
            }
        }
        return false;
    }
};

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 1. 首先,需要在VSCode中安装C++插件,可以在扩展商店中搜索"C++",然后安装Microsoft官方提供的"C++"插件。 2. 接着,需要在本地安装C++编译器,比如gcc或者clang。可以在终端中输入"g++ --version"或者"clang++ --version"来检查是否已经安装。 3. 在VSCode中打开leetcode题目的文件夹,可以使用"Open Folder"命令,或者直接在终端中进入该文件夹。 4. 在VSCode中打开终端,可以使用"Terminal"菜单或者快捷键"Ctrl + ` "。 5. 在终端中输入命令"touch main.cpp",创建一个名为"main.cpp"的文件。 6. 在VSCode中打开"main.cpp"文件,开始编写代码。 7. 编写完代码后,在终端中输入命令"g++ main.cpp -o main",将代码编译成可执行文件。 8. 在终端中输入命令"./main",运行可执行文件,查看代码是否正确。 9. 如果代码正确,可以将代码提交到leetcode网站进行测试。 ### 回答2: VSCode 是一种非常流行的开源代码编辑器,可以用于编写各种计算机编程语言。LeetCode 是一种学习和练习算法的平台,它提供了许多编程问题和测试数据,使得程序员可以通过解决这些问题来提高编程能力。 为了在 VSCode 上 LeetCode,首先我们需要安装 C 语言的编译器和调试器。常见的 C 语言编译器有 gcc 和 clang,它们可以在大多数操作系统上运行,例如 Windows、Mac、Linux 等。同时,为了方便调试,我们可以安装 VSCode 的插件,例如 C/C++、Code Runner 等。 安装好编译器和插件后,我们需要在 VSCode 中创建一个新的 C 语言项目,例如可以在终端中使用命令 `mkdir my_leetcode` 创建一个新的目录,然后在 VSCode 中打开这个目录。接着,在 VSCode 的编辑器中创建一个新的 C 文件,例如 `main.c`,并且在这个文件中编写自己的 LeetCode 解法代码。要注意,我们需要包含正确的头文件,并且使用 main 函数来调用我们编写的算法。 编写完代码后,我们可以使用插件中的 Code Runner 来运行这个程序。在编辑器中按下快捷键 Ctrl+Alt+N,或者通过右键菜单选择 Run Code,就可以编译并运行我们的代码。同时,Code Runner 还可以在终端中显示程序的输出结果,方便调试和查看。 除了 Code Runner,我们还可以使用 VSCode 中内置的调试器来单步运行代码,查看变量的值和程序的执行流程。要使用调试器,我们需要在代码中打断点,然后点击 VSCode 的调试按钮来启动调试器。一旦程序到达断点,就可以逐步进行调试并查看变量的值,从而检查算法的正确性和性能。 总之,使用 VSCode LeetCode 能够较为方便地进行代码编写、调试和运行。我们只需要安装好对应的编译器和插件,然后按照标准的 C 语言写法来编写算法即可。通过不断地练习和掌握,我们能够更好地提高自己的编程水平和算法思维能力。 ### 回答3: VSCode是一种轻量级的代码编辑器,可以为编写代码提供丰富的功能和工具。它具有强大的语法高亮功能,可以轻松地切换不同的语言模式。对于leetcode来说,VSCode可以作为一个开发环境使用,主要有以下几个步骤: 第一步,安装和配置C/C++插件。在VSCode中,您需要安装一个C/C++插件,它可以用来编写和调试C/C++代码。安装该插件后,您需要根据自己的需求进行配置模板,以便更好地适应您的编程风格。 第二步,安装leetcode插件。为了方便,您可以通过在VSCode中安装leetcode插件,轻松访问leetcode网站并题。该插件提供了很多实用功能,包括搜索和筛选题目,提交代码并获得反馈等。 第三步,编写代码。通过安装C/C++插件,您可以在VSCode中轻松编写C/C++代码。在leetcode时,您可以使用VSCode的一些功能,例如代码折叠、语法高亮、代码补全等。 第四步,调试代码。VSCode中的C/C++插件还提供了一个非常强大的调试工具,可以帮助您调试C/C++代码。您可以在断点处暂停代码执行,查看变量值和堆栈信息等。这将大大简化调试过程。 总之,VSCode作为一个轻量级代码编辑器,具有强大的功能和工具,可以帮助您更轻松地leetcode。安装C/C++和leetcode插件以及适当的配置,可以大大提高您的编程效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值