排列组合,回溯训练(配LeetCode训练题链接)

LeetCode77. 组合

LeetCode46. 全排列

LeetCode784. 字母大小写全排列

题目链接直接给大家挂上
回溯:简单的就可以理解为递归的“修改版”,举个例子:如平常的二叉树的遍历,我们在使用dfs时,选择递归边界指针为空。然后执行每个遍历节点的操作(如输出)。执行完成之后我们就没有别的操作了。而回溯便可以在每一步递归完成的时候,回头看看(做一些操作:还原)

回溯和递归的算法设计相同:1.递归边界;2.参数传递;3.每层递归的操作

LeetCode77. 组合
这个题目作为回溯的入门:[1-n],K位数组合,不重复使用,再根据组合的定义,不能有交换位置的结果重复出现(如[2,1] = [1,2])


递归边界:满足k个数
参数传递:向下层递归传递时,排除当前位置 +1
每层操作:把这位数加入到暂存结果,递归传递,这个数已经用过,从暂存结果中删除(回溯)

class Solution {
private:
    vector<vector<int>> result; // 存放符合条件结果的集合
    vector<int> path; // 用来存放符合条件结果
    void backtracking(int n, int k, int startIndex) {
        if (path.size() == k) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i <= n; i++) {
            path.push_back(i); // 处理节点 
            backtracking(n, k, i + 1); // 递归
            path.pop_back(); // 回溯,撤销处理的节点
        }
    }
public:
    vector<vector<int>> combine(int n, int k) {
        backtracking(n, k, 1);
        return result;
    }
};

LeetCode46. 全排列
个人感觉全排列还是比较好写的,直接DFS,注意与组合题区分,元素不能重复使用,但是位置不同就是一个符合条件的情况(如[1,2,3] 和 [1,3,2])是两种不同的结果


递归边界:满足全部数都已经使用
参数传递:向下层递归传递时,排除当前位置 +1
每层操作:用到了访问标志,数组中这个下标的元素没有访问过,才执行这个下标的操作。把这位数加入到暂存结果,递归传递,这个数已经用过,从暂存结果中删除(回溯),不同的是每层我们都要从数组下标0开始,来处理以上的情况(如[1,2,3] 和 [1,3,2])

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;
    vector<bool> st;//标志位
    vector<vector<int>> permute(vector<int>& nums) {
       int n=nums.size();
       for(int i = 0; i < nums.size(); i++)st.push_back(false);//初始时nums[]的每个数字都没别选过,都是false
       dfs(0, nums);
       return ans;
    }
    void dfs(int idx,vector<int>& nums)//u表示选到了path[]的第几个位置了
    {
        if(idx == nums.size())//选完了,将path[]存入ans中
        {
            ans.push_back(path);
            return;
        }
        for(int i = 0; i < nums.size(); i++)
        {
            if(!st[i])//如果nums[i]未被选择过
            {
                st[i] = true;
                path.push_back(nums[i]);//将nums[i]放入path中
                dfs(idx + 1,nums);//进行下一层的搜索
                st[i] = false;//现场的还原
                path.pop_back();//现场的还原
            }
        }
    }

};

LeetCode784. 字母大小写全排列
这个题目和第一个组合题基本是一样的。无非是回溯的操作,以及处理的数据不同


递归边界:访问的下标已经处理完字符串最后一个元素
参数传递:向下层递归传递时,排除当前位置 +1
每层操作:根据题意是大写字母变小写字母,小写字母变大写字母。从当前下标开始,遇到字母执行变型操作,压入结果。进入下一层递归,下层递归结束,把当前字母变型回原来的(回溯)。

class Solution {
public:
    void dfs(string &s, int len, int idx, vector<string> &ans)
    {
        if(idx == len)
            return;
        for(int i = idx; i < len; i++)
        {
            if(s[i] >= 'a' && s[i] <= 'z')
            {    
                s[i] = s[i] - 32;//变大写
                ans.push_back(s);
                dfs(s, len, i + 1, ans);
                s[i] = s[i] + 32;//回溯
            }
            
            if(s[i] >= 'A' && s[i] <= 'Z')
            {    
                s[i] = s[i] + 32;
                ans.push_back(s);
                dfs(s, len, i + 1, ans);
                s[i] = s[i] - 32;//回溯
            }
        }
    }
    vector<string> letterCasePermutation(string s) {
        int len = s.length();
        vector<string> ans;
        ans.push_back(s);
        dfs(s, len, 0, ans);
        return ans;
    }
};

觉得文章有用,点个赞,点个收藏,支持一下

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BiuPsYao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值