LeetCode-回溯

● java是值传递,对象类型变量在传参的过程中复制的是变量的地址,被添加到res,但实际上指向的是同一块内存,stack指向的栈在dfs中只有一份,遍历完成后回到根节点,就成了空列表,所以res.add()的时候要做一次拷贝。

if (depth == len) {
    res.add(new ArrayList<>(path));  //res.add(path) ×
    return;
}

● dfs遍历结束后要回到上一层结点,需要撤销上一次的操作,重置状态和路径stack尾部的值

path.add(nums[i]);
used[i] = true;
dfs(nums, len, depth + 1, path, used, res);

// 注意:下面这两行代码发生 「回溯」,回溯发生在从 深层结点 回到 浅层结点 的过程,代码在形式上和递归之前是对称的

used[i] = false;
path.remove(path.size() - 1);

● 状态变量:begin used[]——排列问题讲究顺序,需要记录哪些数字已经使用,需要used数组;组合问题不讲究顺序,2 2 3与2 3 2视为同一个结果,需要按照某种顺序搜索,需要begin
● 用剪枝提高效率,剪枝需要先排序,对于组合总和问题,总和<当前数组元素时剪枝。画树形图,思考剪枝的条件

题型汇总

题型一:排列、组合、子集相关问题46 47 39 40 77 78 90 60 93
提示:这部分练习可以帮助我们熟悉「回溯算法」的一些概念和通用的解题思路。解题的步骤是:先画图,再编码。去思考可以剪枝的条件, 为什么有的时候用 used 数组,有的时候设置搜索起点 begin 变量,理解状态变量设计的想法。

  1. 全排列(中等)
  2. 全排列 II(中等):思考为什么造成了重复,如何在搜索之前就判断这一支会产生重复;
  3. 组合总和(中等)
  4. 组合总和 II(中等)
  5. 组合(中等)
  6. 子集(中等)
  7. 子集 II(中等):剪枝技巧同 47 题、39 题、40 题;
  8. 第 k 个排列(中等):利用了剪枝的思想,减去了大量枝叶,直接来到需要的叶子结点;
  9. 复原 IP 地址(中等)

[题型二:Flood Fill 200 130 79 733](链接跳转到题目解答)
提示:Flood 是「洪水」的意思,Flood Fill 直译是「泛洪填充」的意思,体现了洪水能够从一点开始,迅速填满当前位置附近的地势低的区域。类似的应用还有:PS 软件中的「点一下把这一片区域的颜色都替换掉」,扫雷游戏「点一下打开一大片没有雷的区域」。

下面这几个问题,思想不难,但是初学的时候代码很不容易写对,并且也很难调试。我们的建议是多写几遍,忘记了就再写一次,参考规范的编写实现(设置 visited 数组,设置方向数组,抽取私有方法),把代码写对。

733.图像渲染(Flood Fill,中等)
200.岛屿数量(中等)
130.被围绕的区域(中等)
79.单词搜索(中等)
说明:以上问题都不建议修改输入数据,设置 visited 数组是标准的做法。

题型三:字符串中的回溯问题 17 22 784
提示:字符串的问题的特殊之处在于,字符串的拼接生成新对象,因此在这一类问题上没有显示「回溯」的过程,但是如果使用 StringBuilder 拼接字符串就另当别论。
在这里把它们单独作为一个题型,是希望朋友们能够注意到这个非常细节的地方。

  1. 电话号码的字母组合(中等),题解;
  2. 字母大小写全排列(中等);
  3. 括号生成(中等) :这道题广度优先遍历也很好写,可以通过这个问题理解一下为什么回溯算法都是深度优先遍历,并且都用递归来写。

题型四:游戏问题 51 37 488 529[困难]
回溯算法是早期简单的人工智能,有些教程把回溯叫做暴力搜索,但回溯没有那么暴力,回溯是有方向地搜索。「力扣」上有一些简单的游戏类问题,解决它们有一定的难度,大家可以尝试一下。

  1. N 皇后(困难):其实就是全排列问题,注意设计清楚状态变量,在遍历的时候需要记住一些信息,空间换时间;
  2. 解数独(困难):思路同「N 皇后问题」;
  3. 祖玛游戏(困难)
  4. 扫雷游戏(困难)

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/
来源:力扣(LeetCode)

你可能感兴趣的:(leetcode,算法)