编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • 数据结构与算法专栏
  • 基础认知

  • 线性结构

  • 树与哈希

  • 工业级实现

  • 算法思想

  • 实战与综合

  • 算法题考核

    • README
    • 数组算法题合集
    • 链表算法题合集
    • 栈队列算法题合集
    • 哈希算法题合集
    • 二叉树算法题合集
    • 堆算法题合集
    • 图算法题合集
    • 二分查找算法题合集
    • 双指针算法题合集
    • 排序算法题合集
    • 分治算法题合集
    • 贪心算法题合集
    • 回溯算法题合集
      • 全排列(Permutations)
        • 01. 题目
        • 02. 题目分析
        • 03. 代码
        • 04. 回溯题型对比
      • 全排列 II(含重复)
        • 01. 题目
        • 02. 分析
        • 03. 代码
      • 子集(Subsets)
        • 01. 题目
        • 02. 分析
        • 03. 代码
        • 04. 回溯三类对比
      • 子集 II(含重复)
        • 01. 题目
        • 02. 代码
      • 组合总和(Combination Sum)
        • 01. 题目
        • 02. 分析
      • 组合总和 II(不可重复+去重)
        • 01. 题目
        • 02. 代码
      • 组合(Combinations) / 电话号码字母组合 / 分割回文串
        • 165 组合 (77)
        • 166 电话号码字母组合 (17)
        • 167 分割回文串 (131)
      • M008 电话号码的字母组合
        • 01. 题目
        • 02. 分析
        • 03. 代码
      • M009 分割回文串 / M010 复原IP / M012 解数独
        • M009 分割回文串 (131)
        • M010 复原IP地址 (93)
        • M012 解数独 (37)
      • N 皇后(N-Queens)
        • 01. 题目
        • 02. 题目分析
        • 03. 代码
      • 解数独(Sudoku Solver)
        • 01. 题目
        • 02. 题目分析
        • 03. 代码
        • 04. N皇后 vs 解数独
    • 动规算法题合集
    • 位运算算法题合集
    • 数学算法题合集
  • 算法
  • 算法题考核
杨充
2021-07-12
目录

回溯算法题合集

# 全排列(Permutations)

LeetCode 46 · ⭐⭐ · 回溯模板

# 01. 题目

[1,2,3] → [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

# 02. 题目分析

回溯决策树

flowchart TD
    A["[]"] --> B["[1]"] & C["[2]"] & D["[3]"]
    B --> B1["[1,2]"] & B2["[1,3]"]
    C --> C1["[2,1]"] & C2["[2,3]"]
    D --> D1["[3,1]"] & D2["[3,2]"]
    B1 --> B1a["[1,2,3]✓"]
    B2 --> B2a["[1,3,2]✓"]
1
2
3
4
5
6
7

回溯通用模板

void backtrack(路径, 选择列表) {
    if (终止条件) { 收集结果; return; }
    for (选择 : 选择列表) {
        做选择;
        backtrack(路径, 新选择列表);
        撤销选择;
    }
}
1
2
3
4
5
6
7
8

# 03. 代码

Java:

List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
    backtrack(nums, new ArrayList<>(), new boolean[nums.length]);
    return res;
}
void backtrack(int[] nums, List<Integer> path, boolean[] used) {
    if (path.size() == nums.length) { res.add(new ArrayList<>(path)); return; }
    for (int i = 0; i < nums.length; i++) {
        if (used[i]) continue;
        path.add(nums[i]); used[i] = true;
        backtrack(nums, path, used);
        path.remove(path.size() - 1); used[i] = false;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Python:

def permute(self, nums):
    res = []
    def dfs(path, used):
        if len(path) == len(nums): res.append(path[:]); return
        for i, n in enumerate(nums):
            if not used[i]: used[i] = True; path.append(n); dfs(path, used); path.pop(); used[i] = False
    dfs([], [False]*len(nums)); return res
1
2
3
4
5
6
7

C++:

vector<vector<int>> permute(vector<int>& nums) {
    vector<vector<int>> res; vector<int> path; vector<bool> used(nums.size());
    function<void()> dfs=[&]{ if(path.size()==nums.size()){ res.push_back(path); return; }
        for(int i=0;i<nums.size();i++){ if(used[i]) continue; used[i]=true; path.push_back(nums[i]); dfs(); path.pop_back(); used[i]=false; } };
    dfs(); return res;
}
1
2
3
4
5
6

复杂度:O(N·N!)/O(N)。

# 04. 回溯题型对比

类型 used/start 特点
全排列(159) used[]数组 顺序重要
子集(161) start索引 每步收集
组合(165) start索引 固定长度

相关:160 全排列II、161 子集

# 全排列 II(含重复)

LeetCode 47 · ⭐⭐ · 回溯+排序去重

# 01. 题目

[1,1,2] → [[1,1,2],[1,2,1],[2,1,1]]

# 02. 分析

去重核心:排序 + i>0 && nums[i]==nums[i-1] && !used[i-1]。

!used[i-1] 保证同层去重(非同一树枝的重复使用)。

# 03. 代码

Java:

public List<List<Integer>> permuteUnique(int[] nums) {
    Arrays.sort(nums);
    List<List<Integer>> res = new ArrayList<>();
    backtrack(nums, new ArrayList<>(), new boolean[nums.length], res);
    return res;
}
void backtrack(int[] nums, List<Integer> path, boolean[] used, List<List<Integer>> res) {
    if (path.size() == nums.length) { res.add(new ArrayList<>(path)); return; }
    for (int i = 0; i < nums.length; i++) {
        if (used[i] || (i > 0 && nums[i] == nums[i-1] && !used[i-1])) continue;
        path.add(nums[i]); used[i] = true;
        backtrack(nums, path, used, res);
        path.remove(path.size()-1); used[i] = false;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Python:

def permuteUnique(self, nums):
    nums.sort(); res = []
    def dfs(path, used):
        if len(path) == len(nums): res.append(path[:]); return
        for i in range(len(nums)):
            if used[i] or (i>0 and nums[i]==nums[i-1] and not used[i-1]): continue
            used[i]=True; path.append(nums[i]); dfs(path,used); path.pop(); used[i]=False
    dfs([],[False]*len(nums)); return res
1
2
3
4
5
6
7
8

C++:

vector<vector<int>> permuteUnique(vector<int>& nums) {
    sort(nums.begin(),nums.end()); vector<vector<int>> res; vector<int> path; vector<bool> used(nums.size());
    function<void()> dfs=[&]{ if(path.size()==nums.size()){ res.push_back(path); return; }
        for(int i=0;i<nums.size();i++){ if(used[i]||(i>0&&nums[i]==nums[i-1]&&!used[i-1])) continue;
            used[i]=true; path.push_back(nums[i]); dfs(); path.pop_back(); used[i]=false; } };
    dfs(); return res;
}
1
2
3
4
5
6
7

复杂度:O(N·N!)/O(N)。

去重类型 条件
全排列去重 !used[i-1](同层)
子集/组合去重 i > start(同层)

相关:159 全排列

# 子集(Subsets)

LeetCode 78 · ⭐⭐ · 回溯 / 位运算

# 01. 题目

[1,2,3] → [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

# 02. 分析

与全排列的区别:①用 start 控制不回头 ②每步都收集结果。

flowchart TD
    A["[] → 收集"] --> B["[1]收集"] & C["[2]收集"] & D["[3]收集"]
    B --> B1["[1,2]收集"] & B2["[1,3]收集"]
    B1 --> B1a["[1,2,3]收集"]
    C --> C1["[2,3]收集"]
1
2
3
4
5

# 03. 代码

Java:

List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) { backtrack(nums,0,new ArrayList<>()); return res; }
void backtrack(int[] nums, int start, List<Integer> path) {
    res.add(new ArrayList<>(path));           // 每步收集
    for (int i = start; i < nums.length; i++) {
        path.add(nums[i]); backtrack(nums, i+1, path); path.remove(path.size()-1);
    }
}
1
2
3
4
5
6
7
8

Python:

def subsets(self, nums):
    res = []
    def dfs(start, path): res.append(path[:]); [dfs(i+1, path+[nums[i]]) for i in range(start, len(nums))]
    dfs(0, [])
    return res
1
2
3
4
5

C++:

vector<vector<int>> subsets(vector<int>& nums) { vector<vector<int>> res; vector<int> path;
    function<void(int)> dfs=[&](int s){ res.push_back(path);
        for(int i=s;i<nums.size();i++){ path.push_back(nums[i]); dfs(i+1); path.pop_back(); } };
    dfs(0); return res; }
1
2
3
4

复杂度:O(N·2^N)/O(N)。

# 04. 回溯三类对比

全排列 子集 组合
收集时机 path.size()==n 每步 path.size()==k
循环起点 0(start无效) start start
used 需要 不需要 不需要

相关:162 子集II、165 组合

# 子集 II(含重复)

LeetCode 90 · ⭐⭐ · 回溯+排序去重

# 01. 题目

[1,2,2] → [[],[1],[1,2],[1,2,2],[2],[2,2]]

# 02. 代码

在子集模板上加:排序 + i>start && nums[i]==nums[i-1](同层去重)。

Java:

public List<List<Integer>> subsetsWithDup(int[] nums) {
    Arrays.sort(nums);
    List<List<Integer>> res = new ArrayList<>();
    backtrack(nums, 0, new ArrayList<>(), res); return res;
}
void backtrack(int[] nums, int start, List<Integer> path, List<List<Integer>> res) {
    res.add(new ArrayList<>(path));
    for (int i = start; i < nums.length; i++) {
        if (i > start && nums[i] == nums[i-1]) continue;
        path.add(nums[i]); backtrack(nums, i+1, path, res); path.remove(path.size()-1);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

Python:

def subsetsWithDup(self, nums):
    nums.sort(); res = []
    def dfs(start, path): res.append(path[:]); [dfs(i+1, path+[nums[i]]) for i in range(start, len(nums)) if not (i>start and nums[i]==nums[i-1])]
    dfs(0, []); return res
1
2
3
4

复杂度:O(N·2^N)/O(N)。

相关:161 子集、160 全排列II

# 组合总和(Combination Sum)

LeetCode 39 · ⭐⭐ · 回溯+可重复选取

# 01. 题目

candidates=[2,3,6,7], target=7 → [[2,2,3],[7]](元素可无限重复)。

# 02. 分析

与子集同框架,区别:①终止条件 remain==0 ②下次递归 start 不+1。

Java:

List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] cand, int target) { backtrack(cand,target,0,new ArrayList<>()); return res; }
void backtrack(int[] cand, int remain, int start, List<Integer> path) {
    if (remain == 0) { res.add(new ArrayList<>(path)); return; }
    if (remain < 0) return;
    for (int i = start; i < cand.length; i++) {
        path.add(cand[i]); backtrack(cand, remain-cand[i], i, path); path.remove(path.size()-1);
    }
}
1
2
3
4
5
6
7
8
9

Python:

def combinationSum(self, cand, target):
    res = []
    def dfs(remain, start, path):
        if remain == 0: res.append(path[:]); return
        if remain < 0: return
        for i in range(start, len(cand)): path.append(cand[i]); dfs(remain-cand[i], i, path); path.pop()
    dfs(target, 0, []); return res
1
2
3
4
5
6
7

复杂度:O(N^(T/M))。关键:backtrack(..., i, ...) 不+1 表示可重复。

相关:164 组合总和II

# 组合总和 II(不可重复+去重)

LeetCode 40 · ⭐⭐ · 回溯+剪枝去重

# 01. 题目

candidates=[10,1,2,7,6,1,5], target=8 → [[1,1,6],[1,2,5],[1,7],[2,6]](每个数只能用一次)。

# 02. 代码

M005 去掉可重复 + 加去重。关键:①start+1 ②排序+i>start && nums[i]==nums[i-1]。

Java:

public List<List<Integer>> combinationSum2(int[] cand, int target) {
    Arrays.sort(cand); List<List<Integer>> res = new ArrayList<>();
    backtrack(cand, target, 0, new ArrayList<>(), res); return res;
}
void backtrack(int[] cand, int remain, int start, List<Integer> path, List<List<Integer>> res) {
    if (remain == 0) { res.add(new ArrayList<>(path)); return; }
    for (int i = start; i < cand.length; i++) {
        if (cand[i] > remain) break;                     // 剪枝
        if (i > start && cand[i] == cand[i-1]) continue; // 去重
        path.add(cand[i]); backtrack(cand, remain-cand[i], i+1, path, res); path.remove(path.size()-1);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

Python:

def combinationSum2(self, cand, target):
    cand.sort(); res = []
    def dfs(remain, start, path):
        if remain == 0: res.append(path[:]); return
        for i in range(start, len(cand)):
            if cand[i] > remain: break
            if i > start and cand[i] == cand[i-1]: continue
            path.append(cand[i]); dfs(remain-cand[i], i+1, path); path.pop()
    dfs(target, 0, []); return res
1
2
3
4
5
6
7
8
9

复杂度:O(N·2^N)。剪枝:cand[i] > remain 可提前 break。

相关:163 组合总和

# 组合(Combinations) / 电话号码字母组合 / 分割回文串

# 165 组合 (77)

n=4, k=2 → [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]

List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> combine(int n, int k) { backtrack(n,k,1,new ArrayList<>()); return res; }
void backtrack(int n, int k, int start, List<Integer> path) {
    if (path.size() == k) { res.add(new ArrayList<>(path)); return; }
    for (int i = start; i <= n - (k - path.size()) + 1; i++) { // 剪枝
        path.add(i); backtrack(n, k, i+1, path); path.remove(path.size()-1);
    }
}
1
2
3
4
5
6
7
8

Python:

def combine(self, n, k):
    res = []
    def dfs(start, path):
        if len(path) == k: res.append(path[:]); return
        for i in range(start, n - (k - len(path)) + 2): path.append(i); dfs(i+1, path); path.pop()
    dfs(1, []); return res
1
2
3
4
5
6

复杂度:O(C(n,k)·k)。剪枝:i <= n-(k-len)-1。

# 166 电话号码字母组合 (17)

digits="23" → ["ad","ae","af","bd","be","bf","cd","ce","cf"]

String[] map = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public List<String> letterCombinations(String digits) {
    List<String> res = new ArrayList<>(); if(digits.isEmpty()) return res;
    backtrack(digits,0,new StringBuilder(),res); return res;
}
void backtrack(String d, int i, StringBuilder sb, List<String> res) {
    if(i==d.length()){ res.add(sb.toString()); return; }
    for(char c:map[d.charAt(i)-'0'].toCharArray()){ sb.append(c); backtrack(d,i+1,sb,res); sb.deleteCharAt(sb.length()-1); }
}
1
2
3
4
5
6
7
8
9

Python:

def letterCombinations(self, digits):
    if not digits: return []
    mp = {'2':'abc','3':'def','4':'ghi','5':'jkl','6':'mno','7':'pqrs','8':'tuv','9':'wxyz'}
    res = []
    def dfs(i, s):
        if i == len(digits): res.append(s); return
        for c in mp[digits[i]]: dfs(i+1, s+c)
    dfs(0, ''); return res
1
2
3
4
5
6
7
8

复杂度:O(4^N·N)/O(N)。

# 167 分割回文串 (131)

s="aab" → [["a","a","b"],["aa","b"]]

List<List<String>> res = new ArrayList<>();
public List<List<String>> partition(String s) { backtrack(s,0,new ArrayList<>()); return res; }
void backtrack(String s, int start, List<String> path) {
    if (start == s.length()) { res.add(new ArrayList<>(path)); return; }
    for (int i = start; i < s.length(); i++)
        if (isPal(s, start, i)) { path.add(s.substring(start, i+1)); backtrack(s, i+1, path); path.remove(path.size()-1); }
}
boolean isPal(String s, int l, int r) { while (l < r) if (s.charAt(l++) != s.charAt(r--)) return false; return true; }
1
2
3
4
5
6
7
8

Python:

def partition(self, s):
    res = []
    def dfs(start, path):
        if start == len(s): res.append(path[:]); return
        for i in range(start, len(s)):
            sub = s[start:i+1]
            if sub == sub[::-1]: path.append(sub); dfs(i+1, path); path.pop()
    dfs(0, []); return res
1
2
3
4
5
6
7
8

相关:159 全排列

# M008 电话号码的字母组合

LeetCode 17 · ⭐⭐ · 回溯

# 01. 题目

digits = "23" → ["ad","ae","af","bd","be","bf","cd","ce","cf"]

# 02. 分析

每个数字映射到一组字母。回溯时对当前数字的每个字母做选择。

# 03. 代码

Java:

String[] map = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public List<String> letterCombinations(String digits) {
    List<String> res = new ArrayList<>();
    if (digits.isEmpty()) return res;
    backtrack(digits, 0, new StringBuilder(), res);
    return res;
}
void backtrack(String digits, int i, StringBuilder sb, List<String> res) {
    if (i == digits.length()) { res.add(sb.toString()); return; }
    for (char c : map[digits.charAt(i) - '0'].toCharArray()) {
        sb.append(c); backtrack(digits, i + 1, sb, res); sb.deleteCharAt(sb.length() - 1);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

Python:

def letterCombinations(self, digits):
    if not digits: return []
    mp = {'2':'abc','3':'def','4':'ghi','5':'jkl','6':'mno','7':'pqrs','8':'tuv','9':'wxyz'}
    res = []
    def dfs(i, s):
        if i == len(digits): res.append(s); return
        for c in mp[digits[i]]: dfs(i+1, s+c)
    dfs(0, '')
    return res
1
2
3
4
5
6
7
8
9

复杂度:O(4^N·N)/O(N)。

相关:M003 子集

# M009 分割回文串 / M010 复原IP / M012 解数独

# M009 分割回文串 (131)

List<List<String>> res=new ArrayList<>();
public List<List<String>> partition(String s) { backtrack(s,0,new ArrayList<>()); return res; }
void backtrack(String s,int start,List<String> path){ if(start==s.length()){ res.add(new ArrayList<>(path)); return; } for(int i=start;i<s.length();i++){ if(isPal(s,start,i)){ path.add(s.substring(start,i+1)); backtrack(s,i+1,path); path.remove(path.size()-1); } } }
boolean isPal(String s,int l,int r){ while(l<r) if(s.charAt(l++)!=s.charAt(r--)) return false; return true; }
1
2
3
4

# M010 复原IP地址 (93)

List<String> ipRes=new ArrayList<>();
public List<String> restoreIpAddresses(String s) { backtrack(s,0,new ArrayList<>()); return ipRes; }
void backtrack(String s,int start,List<String> segs){ if(segs.size()==4&&start==s.length()){ ipRes.add(String.join(".",segs)); return; } if(segs.size()==4||start==s.length()) return; for(int i=1;i<=3&&start+i<=s.length();i++){ String seg=s.substring(start,start+i); if((seg.length()>1&&seg.charAt(0)=='0')||Integer.parseInt(seg)>255) continue; segs.add(seg); backtrack(s,start+i,segs); segs.remove(segs.size()-1); } }
1
2
3

# M012 解数独 (37)

public void solveSudoku(char[][] board) { solve(board); }
boolean solve(char[][] b){ for(int i=0;i<9;i++) for(int j=0;j<9;j++) if(b[i][j]=='.'){ for(char c='1';c<='9';c++){ if(isValid(b,i,j,c)){ b[i][j]=c; if(solve(b)) return true; b[i][j]='.'; } } return false; } return true; }
boolean isValid(char[][] b,int r,int c,char v){ for(int k=0;k<9;k++) if(b[r][k]==v||b[k][c]==v||b[r/3*3+k/3][c/3*3+k%3]==v) return false; return true; }
1
2
3

# N 皇后(N-Queens)

LeetCode 51 · ⭐⭐⭐ · 回溯+约束

# 01. 题目

n×n 棋盘放 n 个皇后,不能同行/同列/同对角线。n=4 有 2 种方案。

# 02. 题目分析

flowchart TD
    A["逐行放置, row=0..n-1"] --> B["对每列col尝试"]
    B --> C{"位置安全?"}
    C -->|YES| D["board[row][col]='Q'"]
    D --> E["递归row+1"]
    E --> F{"找到解?"}
    F -->|row==n| G["收集"]
    C -->|NO| H["换列"]
    E --> I["backtrack: board[row][col]='.'"]
1
2
3
4
5
6
7
8
9

约束检测:只需检查上方、左上对角、右上对角(下方还没放)。

# 03. 代码

Java:

public List<List<String>> solveNQueens(int n) {
    List<List<String>> res = new ArrayList<>();
    char[][] board = new char[n][n];
    for (char[] row : board) Arrays.fill(row, '.');
    backtrack(res, board, 0);
    return res;
}
void backtrack(List<List<String>> res, char[][] b, int row) {
    if (row == b.length) { List<String> list = new ArrayList<>(); for (char[] r : b) list.add(new String(r)); res.add(list); return; }
    for (int col = 0; col < b.length; col++) {
        if (!valid(b, row, col)) continue;
        b[row][col] = 'Q'; backtrack(res, b, row + 1); b[row][col] = '.';
    }
}
boolean valid(char[][] b, int row, int col) {
    for (int i = 0; i < row; i++) if (b[i][col] == 'Q') return false; // 同列
    for (int i=row-1,j=col-1; i>=0&&j>=0; i--,j--) if(b[i][j]=='Q') return false; // 左上
    for (int i=row-1,j=col+1; i>=0&&j<b.length; i--,j++) if(b[i][j]=='Q') return false; // 右上
    return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Python:

def solveNQueens(self, n):
    res, b = [], [['.']*n for _ in range(n)]
    def valid(r, c):
        for i in range(r):
            if b[i][c]=='Q': return False
            if c-(r-i)>=0 and b[i][c-(r-i)]=='Q': return False
            if c+(r-i)<n and b[i][c+(r-i)]=='Q': return False
        return True
    def dfs(r):
        if r==n: res.append([''.join(row) for row in b]); return
        for c in range(n):
            if valid(r,c): b[r][c]='Q'; dfs(r+1); b[r][c]='.'
    dfs(0); return res
1
2
3
4
5
6
7
8
9
10
11
12
13

复杂度:O(N!)/O(N²)。

相关:170 解数独

# 解数独(Sudoku Solver)

LeetCode 37 · ⭐⭐⭐ · 回溯+约束

# 01. 题目

填充 9×9 数独空白格(.),每行/列/3×3宫数字 1-9 不重复。

# 02. 题目分析

flowchart TD
    A["遍历所有格子"] --> B{"找到空白 '.'?"}
    B -->|YES| C["尝试'1'到'9'"]
    C --> D{"合法?"}
    D -->|YES| E["填入 → 递归"]
    E --> F{"递归成功?"}
    F -->|YES| G["true(找到解)"]
    F -->|NO| H["撤销 → 尝试下一个"]
    D -->|NO| H
    B -->|NO(全填满)| I["true(找到解)"]
1
2
3
4
5
6
7
8
9
10

合法判断:(r/3*3 + k/3, c/3*3 + k%3) 定位 3×3 宫。

# 03. 代码

Java:

public void solveSudoku(char[][] board) { solve(board); }
boolean solve(char[][] b) {
    for (int i = 0; i < 9; i++)
        for (int j = 0; j < 9; j++)
            if (b[i][j] == '.') {
                for (char c = '1'; c <= '9'; c++) {
                    if (valid(b, i, j, c)) {
                        b[i][j] = c;
                        if (solve(b)) return true;
                        b[i][j] = '.';
                    }
                }
                return false; // 所有数字都不行
            }
    return true; // 全部填满
}
boolean valid(char[][] b, int r, int c, char v) {
    for (int k = 0; k < 9; k++)
        if (b[r][k] == v || b[k][c] == v || b[r/3*3 + k/3][c/3*3 + k%3] == v)
            return false;
    return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Python:

def solveSudoku(self, b):
    def valid(r, c, v):
        for k in range(9):
            if b[r][k]==v or b[k][c]==v or b[r//3*3+k//3][c//3*3+k%3]==v: return False
        return True
    def solve():
        for i in range(9):
            for j in range(9):
                if b[i][j]=='.':
                    for v in '123456789':
                        if valid(i,j,v): b[i][j]=v; return solve() or (b[i].__setitem__(j,'.') or False)
                    return False
        return True
    solve()
1
2
3
4
5
6
7
8
9
10
11
12
13
14

复杂度:O(9^m),m为空位数。

# 04. N皇后 vs 解数独

N皇后 解数独
回溯模式 逐行 逐格
约束检测 三条线 行+列+宫
终止条件 row==n 全部填满
难度 ⭐⭐⭐ ⭐⭐⭐

相关:169 N皇后

#算法
上次更新: 2026/06/17, 12:46:05
贪心算法题合集
动规算法题合集

← 贪心算法题合集 动规算法题合集→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式