backtrack

  • [22. 括号生成](https://leetcode-cn.com/problems/generate-parentheses/)
  • 39.组合总和
  • 40. 组合总和 II
  • 46. 全排列
  • 47. 全排列 II
  • 78. 子集
  • 递归 t 在递归过程中是深拷贝,不用还原状态。
  • 回溯 t 在回溯过程中是引用,需要还原状态。
  • 90. 子集 II
  • 17. 电话号码的字母组合
  • 22. 括号生成
  • 37. 解数独
  • 39. 组合总和
  • 40. 组合总和 II
  • 46. 全排列
  • 47. 全排列 II
  • 51. N 皇后
  • 52. N皇后 II
  • 77. 组合
  • 78. 子集
  • 79. 单词搜索
  • 89. 格雷编码
  • 90. 子集 II
  • 93. 复原 IP 地址
  • 95. 不同的二叉搜索树 II
  • 113. 路径总和 II
  • 126. 单词接龙 II
  • 131. 分割回文串
  • 140. 单词拆分 II
  • 212. 单词搜索 II
  • 216. 组合总和 III
  • 257. 二叉树的所有路径
  • 282. 给表达式添加运算符
  • 301. 删除无效的括号
  • 306. 累加数
  • 357. 统计各位数字都不同的数字个数
  • 401. 二进制手表
  • 473. 火柴拼正方形
  • 491. 递增子序列
  • 494. 目标和
  • 526. 优美的排列
  • 638. 大礼包
  • 679. 24 点游戏
  • 691. 贴纸拼词
  • 698. 划分为k个相等的子集
  • 784. 字母大小写全排列
  • 797. 所有可能的路径
  • 816. 模糊坐标
  • 980. 不同路径 III
  • 996. 正方形数组的数目
  • 967. 连续差相同的数字
  • 842. 将数组拆分成斐波那契序列
  • 1079. 活字印刷
  • 1096. 花括号展开 II
  • 1219. 黄金矿工
  • 1238. 循环码排列
  • 1239. 串联字符串的最大长度
  • 1240. 铺瓷砖
  • 1255. 得分最高的单词集合
  • 1286. 字母组合迭代器
  • 1307. 口算难题
  • 1415. 长度为 n 的开心字符串中字典序第 k 小的字符串
  • 1467. 两个盒子中球的颜色数相同的概率
  • 1593. 拆分字符串使唯一子字符串的数目最大
  • 1601. 最多可达成的换楼请求数目
  • 1655. 分配重复整数
  • 1718. 构建字典序最大的可行序列
  • 1723. 完成所有工作的最短时间
  • 1774. 最接近目标价格的甜点成本
  • 1799. N 次操作后的最大分数和
  • 1849. 将字符串拆分为递减的连续值
  • 1863. 找出所有子集的异或总和再求和
  • 1947. 最大兼容性评分和
  • 1980. 找出不同的二进制字符串
  • 1986. 完成任务的最少工作时间段
  • 2002. 两个回文子序列长度的最大乘积
  • 2014. 重复 K 次的最长子序列
  • 2044. 统计按位或能得到最大值的子集数目
  • 2048. 下一个更大的数值平衡数
  • 2056. 棋盘上有效移动组合的数目
  • 2065. 最大化一张图中的路径价值
  • 2151. 基于陈述统计最多好人数
  • 2178. 拆分成最多数目的正偶数之和
  • 2305. 公平分发饼干
  • 2375. 根据模式串构造最小数字
  • 2397. 被列覆盖的最多行数
  • LCP 51. 烹饪料理
  • LCP 58. 积木拼接
  • v1066. 校园自行车分配 II
  • v1087. 花括号展开
  • v1088. 易混淆数 II
  • v1215. 步进数
  • v1258. 近义词句子
  • v1820. 最多邀请的个数
  • v2152. 穿过所有点的所需最少直线数量
  • v254. 因子的组合
  • v267. 回文排列 II
  • v291. 单词规律 II
  • v294. 翻转游戏 II
  • v320. 列举单词的全部缩写
  • v351. 安卓系统手势解锁
  • v411. 最短独占单词缩写
  • v425. 单词方块
  • v465. 最优账单平衡
  • v489. 扫地机器人


回溯算法 也叫试探法,它是一种系统地搜索问题的解的方法。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。用回溯算法解决问题的一般步骤为:

  1. 定义一个解空间,它包含问题的解;
  2. 利用适于搜索的方法组织解空间;
  3. 利用深度优先法搜索解空间;
  4. 利用限界函数避免移动到不可能产生解的子空间。

问题的解空间通常是在搜索问题的解的过程中动态产生的,这是回溯算法的一个重要特性。

22. 括号生成

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        
        def backtrack(S, left, right):
            if len(S) == 2 * n:
                ans.append(''.join(S))
                return
            if left < n:
                S.append('(')
                backtrack(S, left + 1, right)
                S.pop()
            if right < left:
                S.append(')')
                backtrack(S, left, right + 1)
                S.pop()
		
		ans = []
        backtrack([], 0, 0)
        return ans

39.组合总和

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:

        def backtrack(idx, target, combinate):
            if target == 0:
                res.append(combinate[:])
                return
            if target < 0: return # ★排序后可去掉

            for i in range(idx, n): # idx 前面的不再重复选取(去重)
                ## 回溯
                # if candidates[i] > target: break # ★排序后可剪枝
                combinate.append(candidates[i])
                backtrack(i, target - candidates[i], combinate) # i 复用
                combinate.pop() 

                ## 拷贝 combinate
                # backtrack(i, target - candidates[i], combinate + [candidates[i]])

        n, res = len(candidates), []
        # candidates.sort() # ★排序
        backtrack(0, target, [])
        return res

40. 组合总和 II

Leetcode

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:

        def backtrack(idx, target, path):
            if target == 0:
                res.append(path[:])
                return

            for i in range(idx, n):
                if candidates[i] > target: break # ★排序后可剪枝
                if i > idx and candidates[i - 1] == candidates[i]:  continue # ★排序后后才能去重

                path.append(candidates[i])
                backtrack(i + 1, target - candidates[i], path)  # i 不复用
                path.pop()

        res, n = [], len(candidates)
        candidates.sort() # ★为了去重
        backtrack(0, target, [])
        return res
class Solution {
    int[] a;
    List<List<Integer>> ans = new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        a = candidates;
        Arrays.sort(a);
        f(0, target, new ArrayList<Integer>());
        return ans;
    }
    void f(int idx, int target, ArrayList<Integer> path){
        if(target == 0){
            ans.add(new ArrayList(path));
            return;
        }
        int pre = -1;
        for(int i = idx; i < a.length; i++){
            if(a[i] > target) break;
            if(pre == a[i]) continue;
            path.add(a[i]);
            f(i + 1, target -a[i], path);
            path.remove(path.size() - 1);
            pre = a[i];
        }
    }
}

46. 全排列

Leetcode「状态变量」:depth or index used path

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        ## 回溯
        def backtrack(depth=0, path=[]):
            if depth == n: res. append(path[:])
                
            for i in range(n):
                if not used[i]:
                    path.append(nums[i])
                    used[i] = True
                    backtrack(depth + 1, path)
                    path.pop()
                    used[i] = False

        ## 回溯
        def backtrack1(depth=0):
            if depth == n: res.append(nums[:])
              
            for i in range(depth, n):
                nums[depth], nums[i] = nums[i], nums[depth]
                backtrack1(depth + 1)
                nums[depth], nums[i] = nums[i], nums[depth]

        ## 深度优先搜索 Depth-First-Search,DFS
        def dfs(depth=0, path=[], tmp=nums):
            if depth == n: res.append(path)
               
            for i in range(len(tmp)):
                dfs(depth + 1, path + [tmp[i]], tmp[:i] + tmp[i + 1:])
        
        n, res = len(nums), []
        used = [False] * n
        backtrack()
        # backtrack1()
        # dfs()

        return res

47. 全排列 II

Leetcode

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        def backtrack(depth=0, path=[]):
            if depth == n: res.append(path[:])
               
            for i in range(n):               
                if used[i] or (i > 0 and nums[i] == nums[i - 1] and used[i - 1]): continue
                
                path.append(nums[i])
                used[i] = True
                backtrack(depth + 1, path)
                path.pop()
                used[i] = False
        
        n, res = len(nums), []  
        nums.sort()         
        used = [False] * n
        backtrack()

        return res

78. 子集

Leetcode

回溯(Backtrack)_List

递归 t 在递归过程中是深拷贝,不用还原状态。

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        def dfs(idx, t):
            res.append(t)
            for i in range(idx, n):
                dfs(i + 1, t + [nums[i]]) # 
        
        res, n = [], len(nums)
        dfs(0, [])
        return res

回溯 t 在回溯过程中是引用,需要还原状态。

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
    	# backtrack 1
        def backtrack(idx):
            # if index == n:  return
            for i in range(idx, n):
                t.append(nums[i])
                res.append(t[:])
                backtrack(i + 1)
                t.pop()
        
        res, t, n = [[]], [], len(nums)
        backtrack(0)
        return res
        
		# backtrack 2
        def backtrack(i):
            if i == n:
                ans.append(t[:])
                return
        
            t.append(nums[i]) 
            backtrack(i + 1) # 选 nums[i]
            t.pop()
            backtrack(i + 1) # 不选 nums[i]
            
        t, ans, n = [], [], len(nums)
        backtrack(0)

        return ans

90. 子集 II

Leetcode

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:

        def backtrack(idx, t):
            res.append(t.copy())
            for i in range(idx, n):
                # 剪枝
                if i > idx and  nums[i] == nums[i - 1]: continue
                t.append(nums[i])
                backtrack(i + 1, t)
                t.pop()

        res, n = [], len(nums)
        nums.sort() # 排序为了去重
        backtrack(0, [])

        return res

4:N皇后问题

class Solution:  
    def solveNqueen(self,n):
        
        res=[] #存放结果组合,对于N皇后问题,这里存放的是其放在每一行对应的列下标      
        def backtrack(combination):
             if len(combination)==n:
                 res.append(combination)
                 return
             for j in range(n):
                 if combination:
                     #排除当前行,列和对应的两个对角线。
                     if j not in combination and j!=combination[-1]+1 and j!=combination[-1]-1:#约束条件
                         backtrack(combination+[j]) #递归回溯
                     else:
                         continue    
                 else:
                    backtrack(combination+[j])                    
                                                   
        backtrack([]) #回溯初始化
        
        #转化为需要的格式
        output=[["." * k + "Q" + "." * (n - k - 1) for k in i] for i in res] #列表生成器
        return output
        
if __name__=='__main__':
    n=4
    solution=Solution()
    print(solution.solveNqueen(n))

leetcode 51 N 皇后问题
在 N*N的棋盘上摆上 N个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,求摆法。
说明:
给定一个整数 n,返回所有不同的 n 皇后的问题解决方案
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中’Q’和’.'分别代表了皇后和空位
示例
输入:4
输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
def solveNQueens(n:int)->List[list[str]]:

#首先我们明确一下皇后的位置如何表示,皇后在 track 中的索引代表它所处的列,它的值表示它所在的行
#比如 track[1]=2,表明第二行第一列放置一个皇后
results=[]

#track 表示路径,xy_dif 和 xy_sum 用来计算两个皇后是否在同一斜线上

def backtrack(track,xy_dif,xy_sum):
    p = len(track)
    if p==n:
        results.append(track)
        return 
    
    for q in range(n):  
        
        #1. 两个皇后不能处在同一列,则它们的索引必然不同(显然)
        #2. 两个皇后不能处在同一行,则它们的值必然不同
        #3. 两个皇后不能处在左上-右下这种位置,则它们横纵坐标之差必然不同(举例,如(3,4)和(4,5)
        #4. 两个皇后不能处在右上-左下这种位置,则它们横纵坐标之和必然不同(举例,如(3,4)和(4,3)
        if (q not in track) and (p-q not in xy_dif) and (p+q not in xy_sum):
            backtrack(track+[q],xy_dif+[p-q],xy_sum+[p+q])
        

backtrack([],[],[])
return [['.'*i + 'Q' + '.' *(n-i-1) for i in result] for result in results]

17. 电话号码的字母组合

22. 括号生成

37. 解数独

39. 组合总和

40. 组合总和 II

46. 全排列

47. 全排列 II

51. N 皇后

52. N皇后 II

77. 组合

78. 子集

79. 单词搜索

89. 格雷编码

90. 子集 II

93. 复原 IP 地址

95. 不同的二叉搜索树 II

113. 路径总和 II

126. 单词接龙 II

131. 分割回文串

140. 单词拆分 II

212. 单词搜索 II

216. 组合总和 III

257. 二叉树的所有路径

282. 给表达式添加运算符

301. 删除无效的括号

306. 累加数

357. 统计各位数字都不同的数字个数

401. 二进制手表

473. 火柴拼正方形

491. 递增子序列

494. 目标和

526. 优美的排列

638. 大礼包

679. 24 点游戏

691. 贴纸拼词

698. 划分为k个相等的子集

784. 字母大小写全排列

797. 所有可能的路径

816. 模糊坐标

980. 不同路径 III

996. 正方形数组的数目

967. 连续差相同的数字

842. 将数组拆分成斐波那契序列

1079. 活字印刷

1096. 花括号展开 II

1219. 黄金矿工

1238. 循环码排列

1239. 串联字符串的最大长度

1240. 铺瓷砖

1255. 得分最高的单词集合

1286. 字母组合迭代器

1307. 口算难题

1415. 长度为 n 的开心字符串中字典序第 k 小的字符串

1467. 两个盒子中球的颜色数相同的概率

1593. 拆分字符串使唯一子字符串的数目最大

1601. 最多可达成的换楼请求数目

1655. 分配重复整数

1718. 构建字典序最大的可行序列

1723. 完成所有工作的最短时间

1774. 最接近目标价格的甜点成本

1799. N 次操作后的最大分数和

1849. 将字符串拆分为递减的连续值

1863. 找出所有子集的异或总和再求和

1947. 最大兼容性评分和

1980. 找出不同的二进制字符串

1986. 完成任务的最少工作时间段

2002. 两个回文子序列长度的最大乘积

2014. 重复 K 次的最长子序列

2044. 统计按位或能得到最大值的子集数目

2048. 下一个更大的数值平衡数

2056. 棋盘上有效移动组合的数目

2065. 最大化一张图中的路径价值

2151. 基于陈述统计最多好人数

2178. 拆分成最多数目的正偶数之和

2305. 公平分发饼干

2375. 根据模式串构造最小数字

2397. 被列覆盖的最多行数

LCP 51. 烹饪料理

LCP 58. 积木拼接

v1066. 校园自行车分配 II

v1087. 花括号展开

v1088. 易混淆数 II

v1215. 步进数

v1258. 近义词句子

v1820. 最多邀请的个数

v2152. 穿过所有点的所需最少直线数量

v254. 因子的组合

v267. 回文排列 II

v291. 单词规律 II

v294. 翻转游戏 II

v320. 列举单词的全部缩写

v351. 安卓系统手势解锁

v411. 最短独占单词缩写

v425. 单词方块

v465. 最优账单平衡

v489. 扫地机器人