回溯法实现各种组合的检索:

今天接触到了回溯法,自己的感觉是这种算法对于某些问题非常合适,比穷举法要有迹可循,而且可以靠到我们学的二叉树等等数据结构上,所以在这里写下自己的理解。

我理解的回溯法其实相当于穷举法的进阶版,它和穷举法解决的问题有些像,比如列出所有可能的组合,尝试找到某种最优解(实在没有啥规律时),但是不同于穷举法,它的实现有点像递归的那种形式,就是按照某种顺序不断前进,但是每前进一步都面临许多情况,所以就把当前的情况当作一个可以继续探索的节点,然后暂时先选一个继续前进直到头,当走到头时,不管有没有走通,回到当时的可探索节点,选择其他的情况继续探索,以此不断前进探索每一个节点。

我上面的描述其实并不规范,但是也说出来了回溯法的许多特点,递归,层次,深度优先搜索,多叉树,其实在用回溯法解决问题的时候搜索方式和对解决问题节点的组织非常容易想到深度搜索和树上。

下面是我在牛客上遇到的一个例题和比较好的实现:

给出一组候选数 C 和一个目标数 T,找出候选数中起来和等于 T 的所有组合。
C 中的每个数字在一个组合中只能使用一次。
 
注意:
  • 题目中所有的数字(包括目标数T )都是正整数
  • 组合中的数字 (a_1, a_2, … , a_ka1,a2,,ak) 要按非递增排序 (a_1 leq a_2 leq … leq a_ka1a2ak).
  • 结果中不能包含重复的组合
  • 组合之间的排序按照索引从小到大依次比较,小的排在前面,如果索引相同的情况下数值相同,则比较下一个索引。
    #include<iostream>
    #include<vector>
    #include<algorithm>
    using namespace std;
    class Solution {
    public:
        vector<vector<int> > combinationSum2(vector<int>& num, int target)
        {
            vector<vector<int>> res;
            vector<int>temp;
            sort(num.begin(), num.end());
            if (num.empty()) return res;
            dfs(num, target, res, tmp, 0);
            return res;
        }
        void dfs(vector<int>& num, int target, vector<vector<int>>& res, vector<int>& temp, int start)//深度搜索算法
        {
            if (target == 0)
            {
                res.push_back(temp);
                return;
            }
            if (start >= num.size())
                return;
            for (int i = start; i < num.size(); i++)
            {
                if (i > start && num[i] == num[i - 1])
                    continue;//注意这一步的判断条件,要达到的目的是为了去除重复,比如
    //原数组为:10 10 20 20 20,目标值为30 我们的函数走到这种情况10 10. 20,好的,回溯到10 10时,此时i=3,会再遇到20时,就会跳过
    if (num[i] <= target) { temp.push_back(num[i]); dfs(num, target - num[i], res, temp, i + 1); temp.pop_back();//这一步实现回溯,就是回到节点,去尝试其他选择(通过i++的方式实现向前走,看起来像深度遍历,只不过是在数组中) } } } };
原文地址:https://www.cnblogs.com/honor260/p/14118412.html