Day33:回溯总结 组合问题 排列问题 切割问题 子集问题 棋盘问题

回溯法就是暴力搜索,并不是什么高效的算法,最多再剪枝一下。

回溯算法能解决如下问题:

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 棋盘问题:N皇后,解数独等等

回溯不好理解,将回溯抽象为树的遍历后思路会更清晰一些

回溯模板:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

一.组合问题:

1.组合问题

对应LeedCode 77.组合

用递归来控制for循环嵌套的个数,收集所有的叶节点

剪枝精髓是:for循环在寻找起点的时候要有一个范围,如果这个起点到集合终止之间的元素已经不够题目要求的k个元素了,就没有必要搜索了。

2. 组合总和问题:

1)对应216.组合总和III
相当于在组合问题上加上和为n的限制,在递归函数参数多加两个值表示当前数相加的总和和目标总和

剪枝思路:已选元素总和如果已经大于n(题中要求的和)了,那么往后遍历就没有意义了,直接剪掉

 2)对应LeedCode的39. 组合总和

Day 27 回溯法 LeedCode:39. 组合总和 40.组合总和II 131.分割回文串-CSDN博客

和上一题的组合总和题的区别在于本题的每个数可以无限制的取,所以递归参数index=i

3)

对应LeedCode40.组合总和II  

Day 27 回溯法 LeedCode:39. 组合总和 40.组合总和II 131.分割回文串-CSDN博客

本题要求元素的每个数只取一次,集合元素会有重复,但要求解集不能包含重复的组合。

本题的特别之处在于如何去重,本题采用树层去重(同一父节点对应的子节点不能有相同的)

树层去重需要先将数组排序,让相对的挨着

if ( i > startIndex && candidates[i] == candidates[i - 1] )continue;

3.多集合求组合问题

对应LeedCode17.电话号码的字母组合,也就是求不同集合之间的组合

Day25:回溯法 LeedCode216.组合总和III 17.电话号码的字母组合-CSDN博客

二.切割问题

对应LeedCode131.分割回文串

Day 27 回溯法 LeedCode:39. 组合总和 40.组合总和II 131.分割回文串-CSDN博客

切割过的地方不能重复切割所以递归函数需要传入i + 1

三.子集问题 

在树形结构中子集问题是要收集所有节点的结果,而组合问题是收集叶子节点的结果

Day28:回溯法 LeedCode 93.复原IP地址 78.子集 90.子集II-CSDN博客

1.对应78.子集问题

数组不包含重复的数

将找子集的回溯的过程抽象为遍历一棵树,那么结果集就是每个结点的集合

2.对应90.子集||

数组包含重复的数,要使得解集不包含重复子集,我们应该对树层去重,而不是树的路径去重

本题的去重:把数组排序,让重复的数排在一起,当前数等于前一数,就跳过当前数,注意第一个数没有前一个数

 3.对应LeedCode491.递增子序列

 Day29:回溯法 491.递增子序列 46.全排列 47.全排列 II-CSDN博客

本题和求子集相似,要求取有序子集,但子集不能重复,所以就不能将原数组排序后取子集实现去重,本题也是同一父节点下的同层上使用过的元素就不能再使用了

同一父节点下的同层上使用过的元素就不能再使用了,在for循环前定义set,递归前在set中查找本层是否已经用过该数,如果没用,将该数加入set,注意set只负责本层,所以进入下一层需要清空

四.排列问题 

Day29:回溯法 491.递增子序列 46.全排列 47.全排列 II-CSDN博客

1.对应leedCode46.全排列

不含重复数字的数组

用回溯法收集叶节点,用uesd数组记录路径上哪些数字已经使用,实现路径去重,这里就不是树层去重了

2.对应LeedCode47.全排列II

含有重复数字的数组

本题与上一题区别在于,序列nums包含重复数字, 本题该如何去重呢?

先将数组排序,为了方便实现树层去重

  1.  used[i - 1] == true,说明同⼀树⽀nums[i - 1]使⽤过

  2.  used[i - 1] == false,说明同⼀树层nums[i - 1]使⽤过

  3.  如果同⼀树层nums[i - 1]使⽤过则直接跳过

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

五.棋盘问题

Day30 回溯 LeedCode 332.重新安排行程 51. N皇后 37. 解数独-CSDN博客

1.一维递归

对应leedCode51.N皇后问题]

n皇后问题一行只需要放一个皇后,for循环遍历列即可,递归函数参数传入行

2.二维递归

对应LeedCode37.独数解问题

一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!

  • 29
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
子集问题是一个经典的组合问题,给定一个正整数集合和一个目标值,找出集合中若干个数的和等于目标值。这个问题可以使用回溯算法来解决。 回溯算法的思路是,在搜索过程中维护一个当前的集合和当前的和,从集合中选择一个数加入集合,如果加入后集合的和等于目标值,就找到了一组解;否则继续选择下一个数加入集合,直到遍历完所有的选择或者集合的和已经超过了目标值,此时需要回溯到上一个状态,重新选择下一个数加入集合。 具体的实现可以使用递归函数来完成。递归函数的参数包括当前的集合、当前的和、目标值、集合中数的起始下标。在递归函数中,首先判断当前集合的和是否等于目标值,如果是则找到了一组解,否则继续向下搜索。在搜索过程中,维护一个变量表示当前集合的和,然后选择一个数加入集合,更新集合和,递归调用自身,搜索下一个数,直到遍历完所有的选择。 代码如下: ```python def subset_sum(nums, target): res = [] def backtrack(subset, start, cur_sum): if cur_sum == target: res.append(subset[:]) elif cur_sum < target: for i in range(start, len(nums)): subset.append(nums[i]) cur_sum += nums[i] backtrack(subset, i+1, cur_sum) subset.pop() cur_sum -= nums[i] backtrack([], 0, 0) return res ``` 这个算法的时间复杂度是指数级别的,因为在搜索过程中每个数都有选或不选两种选择,所以总共有 $2^n$ 种情况需要搜索,其中 $n$ 是集合中数的个数。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值