回溯算法总结

回溯法,⼀般可以解决如下⼏种问题:

  • 组合问题:N个数⾥⾯按⼀定规则找出k个数的集合
  • 切割问题:⼀个字符串按⼀定规则有⼏种切割⽅式
  • ⼦集问题:⼀个N个数的集合⾥有多少符合条件的⼦集
  • 排列问题:N个数按⼀定规则全排列,有⼏种排列⽅式
  • 棋盘问题:N皇后,解数独等等

组合是不强调元素顺序的,排列是强调元素顺序


回溯法⼀般是在集合中递归搜索,集合的⼤⼩构成了树的宽度,递归的深度构成的
树的深度。

回溯算法.png

回溯算法模板框架如下:

void backtracking(参数) {
 if (终⽌条件) {
 存放结果;
 return;
 }
 for (选择:本层集合中元素(树中节点孩⼦的数量就是集合的⼤⼩)) {
 处理节点;
 backtracking(路径,选择列表); // 递归
 回溯,撤销处理结果
 }
}

组合问题

组合
题⽬链接:https://leetcode-cn.com/problems/combinations/
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:
输⼊: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

思路

把组合问题抽象为如下树形结构:


组合问题.png

可以看出这个棵树,⼀开始集合是 1,2,3,4, 从左向右取数,取过的数,不在重复取。
第⼀次取1,集合变为2,3,4 ,因为k为2,我们只需要再取⼀个数就可以了,分别取2,3,4,得到集
合[1,2] [1,3] [1,4],以此类推。

每次从集合中选取元素,可选择的范围随着选择的进⾏⽽收缩,调整可选择的范围
图中可以发现n相当于树的宽度,k相当于树的深度

那么如何在这个树上遍历,然后收集到我们要的结果集呢?
图中每次搜索到了叶⼦节点,我们就找到了⼀个结果。
相当于只需要把达到叶⼦节点的结果收集起来,就可以求得 n个数中k个数的组合集合。


组合问题如何进⾏剪枝?

举⼀个例⼦,n = 4,k = 4的话,那么第⼀层for循环的时候,从元素2开始的遍历都没有意义了。 在第⼆层for循环,从元素3开始的遍历都没有意义了。


组合问题剪枝优化.png

所以,可以剪枝的地⽅就在递归中每⼀层的for循环所选择的起始位置。
如果for循环选择的起始位置之后的元素个数 已经不⾜ 我们需要的元素个数了,那么就没有必要搜索了。

一般遍历的代码如下

for (int i = startIndex; i <= n; i++) {
    dfs(下一层)
}

下一层的startindex怎么取?
一般都需要startIndex来控制for循环的起始位置,对于组合问题,什么时候需要startIndex呢?
如果是⼀个集合来求组合的话,就需要startIndex,例如:回溯算法:求组合问题!,回
溯算法:求组合总和!。
如果是多个集合取组合,各个集合之间相互不影响,那么就不⽤startIndex,例如:回溯算法:电话号码的字⺟组合

此外,在求和问题中,排序之后加剪枝是常⻅的套路


去重问题

都知道组合问题可以抽象为树形结构,那么“使⽤过”在这个树形结构上是有两个维度的,⼀个维度是同⼀树枝上使⽤过,⼀个维度是同⼀树层上使⽤过。没有理解这两个层⾯上的“使⽤过” 是造成⼤家没有彻底理解去重的根本原因。

如元素在同⼀个组合内是可以重复的,怎么重复都没事,但两个组合不能相同
所以我们要去重的是同⼀树层上的“使⽤过”,同⼀树枝上的都是⼀个组合⾥的元素,不⽤去重

强调⼀下,树层去重的话,需要对数组排序!

来举⼀个例⼦,candidates = [1, 1, 2], target = 3,(⽅便起⻅candidates已经排序
了)

选择过程树形结构如图所示:


去重.png

如果 candidates[i] == candidates[i - 1] 并且 used[i - 1] == false ,就说明:前⼀个树枝,使⽤了candidates[i - 1],也就是说同⼀树层使⽤过candidates[i - 1]。

此时for循环⾥就应该做continue的操作。
这块⽐较抽象,如图:


去重2.png

我在图中将used的变化⽤橘⻩⾊标注上,可以看出在candidates[i] == candidates[i - 1]相同的情况下:

  • used[i - 1] == true,说明同⼀树⽀candidates[i - 1]使⽤过
  • used[i - 1] == false,说明同⼀树层candidates[i - 1]使⽤过
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容