leetcode算法总结 —— 回溯

参考连接
https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/

模版

pathV可反复使用,他相当于整个遍历中的一个容器,我们前序遍历向容器中加入元素,回溯的时候后序遍历,从容器中去掉元素

    void backtrack(vector<int> &nums,vector<int> &pathV)
    {
    
    
        //1. 结束条件:并加入结果集
        if (nums.size() == pathV.size())
        {
    
    
            vvRes.push_back(pathV);
            return;
        }
        //2. 选择列表中的所有值进行遍历,也就是子树中能够选择的值
        for (int i = 0; i < nums.size(); i++)
        {
    
    
            // 3. 判断合法如果该路径不合法则不对该路径进行递归了
            if (!isValid(pathV, nums, i)) continue;
            
            //4. 前序遍历,添加值,该处使用的前序遍历,会先走到最左子节点
            pathV.push_back(nums[i]);
            backtrack(pathV, nums);
            //5. 后序遍历,按左右根的顺序,向上返回时,去掉当前值
            //回溯:popback让他退回到父节点的状态,因为pathV还要再次使用
            pathV.pop_back();
        }
    }

例题

  1. 全排列
class Solution
{
    
    
public:
    //回溯的本质是DFS
    vector<vector<int>> vvRes;
    vector<vector<int>> permute(vector<int> &nums)
    {
    
    
        vector<int> pathV;
        vector<bool> used(nums.size(), false);
        backtrack(nums,pathV, used);
        return vvRes;
    }
    //pathV为每次父节点传给子节点
    void backtrack(vector<int> &nums, vector<int> &pathV, vector<bool> &used)
    {
    
    
        //结束条件:路径的长度等于数组长度,填满了就结束
        if (nums.size() == pathV.size())
        {
    
    
            vvRes.push_back(pathV); //把该条路径的结果添加到res中
            return;
        }
        //选择列表中的所有值进行遍历,也就是子树中能够选择的值
        for (int i = 0; i < nums.size(); i++)
        {
    
    
            // 如果使用过了该元素,就不向下遍历了
            if(used[i] == true) continue;
            used[i] = true;
            //前序遍历,添加值,该处使用的前序遍历,会先走到最左子节点
            pathV.push_back(nums[i]);
            backtrack(nums, pathV,used);
            //后序遍历,按左右根的顺序,向上返回时,去掉当前值
            //回溯:popback让他退回到父节点的状态,因为pathV还要再次使用
            pathV.pop_back();
            used[i] = false;
        }
    }
    
};
    1. 组合总和
class Solution
{
    
    
//https://leetcode-cn.com/problems/combination-sum/solution/shou-hua-tu-jie-zu-he-zong-he-combination-sum-by-x/
public:
    int targetM = 0;
    vector<vector<int>> vvRes;
    vector<vector<int>> combinationSum(vector<int> &candidates, int target)
    {
    
    
        targetM = target;
        vector<int> path;
        backtrack(candidates, path,0,0);
        return vvRes;
    }
    
    void backtrack(vector<int> &candidates, vector<int> &path, int index,int pathSum)
    {
    
    
        //结束条件:当target == sum加入结果不需要遍历
        if (targetM == pathSum)
        {
    
    
            vvRes.push_back(path);
            return;
        }
        //结束条件2:如果当前和大于目标值就不需要向下遍历了
        if (pathSum > targetM)  return;

        //选择列表:注意横向,也就是同级别节点下一个数不会选择当前数
        for (int i = index; i < candidates.size(); i++) 
        {
    
    
            //for循环里不需要剪枝,因为选择列表无需改变
            path.push_back(candidates[i]);
            //这里i不需要+1,也就是纵向,向下遍历的时候还可以选当前数
            backtrack(candidates, path, i, pathSum + candidates[i]);
            path.pop_back();
        }
        
    }
};
    1. 子集
class Solution {
    
    
public:
    vector<vector<int>> res;
    vector<vector<int>> subsets(vector<int>& nums) {
    
    
        vector<int> pathV;
        res.push_back(pathV);
        backTrack(nums,pathV,0);
        return res;
    }
    void backTrack(vector<int>& nums, vector<int>& pathV, int start) {
    
    
        //1.结束条件:不再向下遍历开始向上返回的条件
        if(nums.size() == pathV.size()) {
    
    
            return;
        }
        //2.遍历,选择列表,该题的选择列表
        //注意这个选择列表,我们设置i=start表示我们横向同级别遍历2的时候不选当前树
        //而下面的i+1是向子树遍历的时候不选当前数,注意区别
        for(int i = start; i < nums.size(); i++) {
    
    
            //3. 判断合法,是否使用过,如果使用过了,就不要了,这里都合法
            //4.先序和后序
            //如果没使用过
            pathV.push_back(nums[i]);
            res.push_back(pathV);
            backTrack(nums,pathV,i+1);//这里+1表示向子树遍历的时候不能够再选当前数了
            pathV.pop_back();
        }
    }
};

猜你喜欢

转载自blog.csdn.net/chongbin007/article/details/115414107
今日推荐