回溯法适用于求解组合数量较大的问题_回溯算法几个经典例子[通俗易懂]

回溯法适用于求解组合数量较大的问题_回溯算法几个经典例子[通俗易懂]本题也是组合问题的一种,之前做过的组合问题是给定几个数字n让我们找到k个数的组合,这次则是给定了数组与目标值,考虑应该怎么去求解

算法笔记

本题也是组合问题的一种,之前做过的组合问题是给定几个数字n让我们找到k个数的组合,这次则是给定了数组与目标值,考虑应该怎么去求解。本题仍然是跟随代码随想录进行学习,视频链接为组合总和。

题目描述

在这里插入图片描述
理解一下题目,题目中给定了一个不含有重复元素的数组candidates以及目标值target,要求让我们找到数组当中的和为target的所有组合,并且每个组合当中的原数组里的数字允许被重复选取。

解题过程

之前我们谈到了回溯的模板以及解题的三个步骤,下图先放出模板用于回忆。

void backtracking(参数) { 
   
	if (结束条件) { 
   
	存结果并且返回;
	}
	for(横向遍历) { 
   
	处理节点;
	backtracking(递归);
	关键步骤回溯;
	}
}

其次我们构建树,下图为我们构造的树,图里面打叉的与涂黑的部分都是我们不需要的,我们要的是最终加和为目标值的总和,所以三部曲就可以开始了。
在这里插入图片描述

1.选择参数:与之前一样需要一个记录每条路径的一维数组path以及返回最后结果的二维数组result,然后我们要多考虑的部分就是加和,所以我们要额外定义一个sum来表示总和,我们代码中将这些都设置为全局变量,另外我们的回溯函数中也需要startIndex去记录节点,与上题类似。

2.终止条件:看图就很好理解,如果我们的sum大于target就说明这个组合没什么用了,直接return,如果sum==target就让result数组去记录这个组合,同时也要return。

3.循环递归回溯:这部分内容也是与之前类似,给定一个for循环,先用path数组收集我们的每一个点,使用sum加上每一个点来与target进行判断,随后回溯,记住题目要求数组可以重复使用所以我们不用同之前一样去在参数中对i+1,直接i就可以了,回溯完撤销节点,与模板中的过程时是差不多的,这里要记得sum还要将之前加的部分退出去,不然撤销的节点就会发生变化,至此任务就完成了。

代码部分

class Solution { 
   
public:

    vector<int> path;
    vector<vector<int>> result;
    int sum;

    void backtracking(vector<int>& candidates, int target, int startIndex) { 
   
        if(sum > target) { 
   
            return;
        }
        if(sum == target) { 
   
            result.push_back(path);
            return;
        }

        for(int i = startIndex; i < candidates.size(); i++) { 
   
            path.push_back(candidates[i]);
            sum += candidates[i];
            backtracking(candidates, target, i);
            sum -= candidates[i];
            path.pop_back();
        }

    }

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) { 
   
        backtracking(candidates, target, 0);
        return result;
    }
};

剪枝优化

给定的数组如果是有序的数组我们可以发现,每次选取元素的时候,我们左边得到的组合【这里认为顺序从小到大】往往会更深,而右边较大的部分则会提前满足target或者大于target,所以先对整个数组进行排序就可以更清晰地梳理我们的树,之后我们去考虑for循环中i的条件,1.2中学习过剪枝基本就是对于for循环的条件进行处理。在终止条件中,如果sum大于target就跳出循环,但是在for循环中递归仍然进行,同时sum是每一层candidates的加和,所以可以判断 sum + candidates[i] <= targe就是我们所需要的条件,至此操作完成,代码如下:

class Solution { 
   
public:

    vector<int> path;
    vector<vector<int>> result;
    int sum;

    void backtracking(vector<int>& candidates, int target, int startIndex) { 
   
        if(sum > target) { 
   
            return;
        }
        if(sum == target) { 
   
            result.push_back(path);
            return;
        }
        for(int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++){ 
   
            path.push_back(candidates[i]);
            sum += candidates[i];
            backtracking(candidates, target, i);
            sum -= candidates[i];
            path.pop_back();
        }
    }

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) { 
   
        sort(candidates.begin(), candidates.end());
        backtracking(candidates, target, 0);
        return result;
    }
};

结语

我个人认为本题是比较容易理解的,大致的代码逻辑也与上一题比较相似,但是在剪枝这一部分当中的思考就会比较灵活,我只片面的想到了在for循环的条件中判断一层中如果sum值已经大于target就没有必要再进行下去,未考虑到排序,所以仍需要不断积累经验去完善整部分代码。

今天的文章回溯法适用于求解组合数量较大的问题_回溯算法几个经典例子[通俗易懂]分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/88157.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注