代码随想录第三十天|回溯算法总结

回溯算法模板:

void backtracking(参数) {
      if(终止条件) {
      存放结果;
      return;
      }
      for(选择;本层集合中元素) {
      处理节点;
      backtracking(路径,选择列表);
      回溯,撤销处理结果;
      }
}
  • 组合问题

N个数里面按照一定规律找出k个数。(集合是具有无序性的)

①给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

②找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

③给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

④给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为target的组合。candidates中的每个数字在每个组合中只能使用一次。

for循环进行横向遍历,递归进行纵向遍历,回溯不断调整结果集。

最后收集叶子结点的结果

①、②解释

①和②的截止条件都为path的个数为k。区别在于②在k个数相加和为n时才放入result,①全部可以放入。

在进行for循环时,①和②都需要从startIndex开始并且for循环中的回溯传入的应为i+1(不是i),因为,数组中的每个元素不能重复使用。

还可进行剪枝优化操作:

//①
for(int i=startIndex;i<=n-(k-path.size())+1;i++) 
//②
for(int i=startIndex;i<=9-(k-path.size())+1;i++) 

①:

 

②:

 ③、④解释

③和④没有个数的限制,因此终止条件为和大于等于目标值,若等于目标值,放入result集合中;若不等于目标值,则不放入。

if(sum>=target) {
            if(sum==target) result.push_back(path);
            return;
        }

③中candidates数组没有重复元素并且元素可重复利用。因此backtracking中传入的为i。

④中candidates数组有重复元素并且每个元素只能使用一次,因此在进行回溯时需要去重,该去重是对同一树层进行去重,但同一树枝不用进行去重(因为所求数组中可以出现两个相同的元素,因为candidates中可能包含数值相同但不是同一个的元素)。

 

可利用used数组进行树层去重(利用该方法去重需对candidates数组进行排列,确保相同数值的元素挨在一起)

sort(candidates.begin(),candidates.end());

最开始令uesd数组中的元素都为false,每次取一个元素,该位置的used变为true,进入下一层取值,跳出该层循环后该位置的uesd又变回false。因此当used为false,且下一位置元素与该位置元素相等,说明是树层间元素重复,需要去重(下一位置操作应该跳出循环),当used为true,且下一位置元素与当前位置元素重复说明是树枝元素数值重复,无需去重。

 if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false) continue;

used[i - 1] == true,说明同一树枝candidates[i - 1]使用过

used[i - 1] == false,说明同一树层candidates[i - 1]使用过

  • 切割问题

一个字符串按一定规则有几种切割方式。

①给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

  • 子集问题

一个N个数的集合里有多少符合条件的子集。

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

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

③给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。

在树形结构中,子集问题是收集所有节点的结果。

①、②解释:

①与②的区别在于①中没有重复元素,②中有重复元素,需要去重。

①中只要收集到path就放入result中,在for循环中,回溯传入的为i+1,因为数组中不能重复选取元素。

在②中需对树层进行去重操作,利用used数组方法进行去重,操作与组合问题相同。

 ③中与②的区别为③不可以进行排列,因为排列后数组顺序发生改变,不能看是否为递增序列。因此,利用unordered_set对同层元素进行去重。

if ((!path.empty() && nums[i] < path.back())|| uset.find(nums[i]) != uset.end()) 
            {
                    continue;
            }
  • 排列问题

N个数按一定规则全排列,有几种排列方式。

①给定一个 没有重复 数字的序列,返回其所有可能的全排列。

②给定一个可包含重复数字的序列,按任意顺序返回所有不重复的全排列。

①与②的区别在于二需要去重

在①中,利用uesd数组记录使用过的元素,当前层使用过该元素过后,令used[i]=true,进入下一层后遇到true跳出循环,完成排列操作。

if(used[i]==true) continue;

 ②可利用used数组对其进行去重操作。

if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false) continue;
if(used[i]==true) continue;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值