Leetcode 题解 - 搜索.md 35.9 KB
Newer Older
C
CyC2018 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
<!-- GFM-TOC -->
* [BFS](#bfs)
    * [计算在网格中从原点到特定点的最短路径长度](#计算在网格中从原点到特定点的最短路径长度)
    * [组成整数的最小平方数数量](#组成整数的最小平方数数量)
    * [最短单词路径](#最短单词路径)
* [DFS](#dfs)
    * [查找最大的连通面积](#查找最大的连通面积)
    * [矩阵中的连通分量数目](#矩阵中的连通分量数目)
    * [好友关系的连通分量数目](#好友关系的连通分量数目)
    * [填充封闭区域](#填充封闭区域)
    * [能到达的太平洋和大西洋的区域](#能到达的太平洋和大西洋的区域)
* [Backtracking](#backtracking)
    * [数字键盘组合](#数字键盘组合)
    * [IP 地址划分](#ip-地址划分)
    * [在矩阵中寻找字符串](#在矩阵中寻找字符串)
    * [输出二叉树中所有从根到叶子的路径](#输出二叉树中所有从根到叶子的路径)
    * [排列](#排列)
    * [含有相同元素求排列](#含有相同元素求排列)
    * [组合](#组合)
    * [组合求和](#组合求和)
    * [含有相同元素的求组合求和](#含有相同元素的求组合求和)
    * [1-9 数字的组合求和](#1-9-数字的组合求和)
    * [子集](#子集)
    * [含有相同元素求子集](#含有相同元素求子集)
    * [分割字符串使得每个部分都是回文数](#分割字符串使得每个部分都是回文数)
    * [数独](#数独)
    * [N 皇后](#n-皇后)
<!-- GFM-TOC -->


C
CyC2018 已提交
31 32
深度优先搜索和广度优先搜索广泛运用于树和图中,但是它们的应用远远不止如此。

C
CyC2018 已提交
33
# BFS
C
CyC2018 已提交
34

C
CyC2018 已提交
35
<div align="center"> <img src="pics/95903878-725b-4ed9-bded-bc4aae0792a9.jpg"/> </div><br>
C
CyC2018 已提交
36 37 38 39 40

广度优先搜索一层一层地进行遍历,每层遍历都以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点。需要注意的是,遍历过的节点不能再次被遍历。

第一层:

C
CyC2018 已提交
41
- 0 -> {6,2,1,5}
C
CyC2018 已提交
42 43 44

第二层:

C
CyC2018 已提交
45 46 47 48
- 6 -> {4}
- 2 -> {}
- 1 -> {}
- 5 -> {3}
C
CyC2018 已提交
49 50 51

第三层:

C
CyC2018 已提交
52 53
- 4 -> {}
- 3 -> {}
C
CyC2018 已提交
54

C
CyC2018 已提交
55
每一层遍历的节点都与根节点距离相同。设 d<sub>i</sub> 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 d<sub>i</sub> <= d<sub>j</sub>。利用这个结论,可以求解最短路径等  **最优解**  问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径。
C
CyC2018 已提交
56

C
CyC2018 已提交
57
在程序实现 BFS 时需要考虑以下问题:
C
CyC2018 已提交
58

C
CyC2018 已提交
59 60
- 队列:用来存储每一轮遍历得到的节点;
- 标记:对于遍历过的节点,应该将它标记,防止重复遍历。
C
CyC2018 已提交
61

C
CyC2018 已提交
62
## 计算在网格中从原点到特定点的最短路径长度
C
CyC2018 已提交
63 64 65

```html
[[1,1,0,1],
C
CyC2018 已提交
66 67 68
 [1,0,1,0],
 [1,1,1,1],
 [1,0,1,1]]
C
CyC2018 已提交
69 70
```

C
CyC2018 已提交
71
1 表示可以经过某个位置,求解从 (0, 0) 位置到 (tr, tc) 位置的最短路径长度。
C
CyC2018 已提交
72 73

```java
C
CyC2018 已提交
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
public int minPathLength(int[][] grids, int tr, int tc) {
    final int[][] direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    final int m = grids.length, n = grids[0].length;
    Queue<Pair<Integer, Integer>> queue = new LinkedList<>();
    queue.add(new Pair<>(0, 0));
    int pathLength = 0;
    while (!queue.isEmpty()) {
        int size = queue.size();
        pathLength++;
        while (size-- > 0) {
            Pair<Integer, Integer> cur = queue.poll();
            int cr = cur.getKey(), cc = cur.getValue();
            grids[cr][cc] = 0; // 标记
            for (int[] d : direction) {
                int nr = cr + d[0], nc = cc + d[1];
                if (nr < 0 || nr >= m || nc < 0 || nc >= n || grids[nr][nc] == 0) {
                    continue;
                }
                if (nr == tr && nc == tc) {
                    return pathLength;
                }
                queue.add(new Pair<>(nr, nc));
            }
        }
    }
    return -1;
C
CyC2018 已提交
100 101 102
}
```

C
CyC2018 已提交
103
## 组成整数的最小平方数数量
C
CyC2018 已提交
104

C
CyC2018 已提交
105
[279. Perfect Squares (Medium)](https://leetcode.com/problems/perfect-squares/description/)
C
CyC2018 已提交
106 107

```html
C
CyC2018 已提交
108
For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.
C
CyC2018 已提交
109 110 111 112
```

可以将每个整数看成图中的一个节点,如果两个整数之差为一个平方数,那么这两个整数所在的节点就有一条边。

C
CyC2018 已提交
113
要求解最小的平方数数量,就是求解从节点 n 到节点 0 的最短路径。
C
CyC2018 已提交
114 115 116 117

本题也可以用动态规划求解,在之后动态规划部分中会再次出现。

```java
C
CyC2018 已提交
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
public int numSquares(int n) {
    List<Integer> squares = generateSquares(n);
    Queue<Integer> queue = new LinkedList<>();
    boolean[] marked = new boolean[n + 1];
    queue.add(n);
    marked[n] = true;
    int level = 0;
    while (!queue.isEmpty()) {
        int size = queue.size();
        level++;
        while (size-- > 0) {
            int cur = queue.poll();
            for (int s : squares) {
                int next = cur - s;
                if (next < 0) {
                    break;
                }
                if (next == 0) {
                    return level;
                }
                if (marked[next]) {
                    continue;
                }
                marked[next] = true;
                queue.add(next);
            }
        }
    }
    return n;
C
CyC2018 已提交
147 148 149
}

/**
C
CyC2018 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162
 * 生成小于 n 的平方数序列
 * @return 1,4,9,...
 */
private List<Integer> generateSquares(int n) {
    List<Integer> squares = new ArrayList<>();
    int square = 1;
    int diff = 3;
    while (square <= n) {
        squares.add(square);
        square += diff;
        diff += 2;
    }
    return squares;
C
CyC2018 已提交
163 164 165
}
```

C
CyC2018 已提交
166
## 最短单词路径
C
CyC2018 已提交
167

C
CyC2018 已提交
168
[127. Word Ladder (Medium)](https://leetcode.com/problems/word-ladder/description/)
C
CyC2018 已提交
169 170 171

```html
Input:
C
CyC2018 已提交
172 173 174
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
C
CyC2018 已提交
175

C
CyC2018 已提交
176
Output: 5
C
CyC2018 已提交
177

C
CyC2018 已提交
178 179
Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
C
CyC2018 已提交
180 181 182 183
```

```html
Input:
C
CyC2018 已提交
184 185 186
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
C
CyC2018 已提交
187

C
CyC2018 已提交
188
Output: 0
C
CyC2018 已提交
189

C
CyC2018 已提交
190
Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
C
CyC2018 已提交
191 192
```

C
CyC2018 已提交
193
题目描述:找出一条从 beginWord 到 endWord 的最短路径,每次移动规定为改变一个字符,并且改变之后的字符串必须在 wordList 中。
C
CyC2018 已提交
194 195

```java
C
CyC2018 已提交
196 197 198 199 200 201 202 203 204 205 206 207 208
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
    wordList.add(beginWord);
    int N = wordList.size();
    int start = N - 1;
    int end = 0;
    while (end < N && !wordList.get(end).equals(endWord)) {
        end++;
    }
    if (end == N) {
        return 0;
    }
    List<Integer>[] graphic = buildGraphic(wordList);
    return getShortestPath(graphic, start, end);
C
CyC2018 已提交
209 210
}

C
CyC2018 已提交
211 212 213 214 215 216 217 218 219 220 221 222
private List<Integer>[] buildGraphic(List<String> wordList) {
    int N = wordList.size();
    List<Integer>[] graphic = new List[N];
    for (int i = 0; i < N; i++) {
        graphic[i] = new ArrayList<>();
        for (int j = 0; j < N; j++) {
            if (isConnect(wordList.get(i), wordList.get(j))) {
                graphic[i].add(j);
            }
        }
    }
    return graphic;
C
CyC2018 已提交
223 224
}

C
CyC2018 已提交
225 226 227 228 229 230 231 232
private boolean isConnect(String s1, String s2) {
    int diffCnt = 0;
    for (int i = 0; i < s1.length() && diffCnt <= 1; i++) {
        if (s1.charAt(i) != s2.charAt(i)) {
            diffCnt++;
        }
    }
    return diffCnt == 1;
C
CyC2018 已提交
233 234
}

C
CyC2018 已提交
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
private int getShortestPath(List<Integer>[] graphic, int start, int end) {
    Queue<Integer> queue = new LinkedList<>();
    boolean[] marked = new boolean[graphic.length];
    queue.add(start);
    marked[start] = true;
    int path = 1;
    while (!queue.isEmpty()) {
        int size = queue.size();
        path++;
        while (size-- > 0) {
            int cur = queue.poll();
            for (int next : graphic[cur]) {
                if (next == end) {
                    return path;
                }
                if (marked[next]) {
                    continue;
                }
                marked[next] = true;
                queue.add(next);
            }
        }
    }
    return 0;
C
CyC2018 已提交
259 260 261
}
```

C
CyC2018 已提交
262
# DFS
C
CyC2018 已提交
263

C
CyC2018 已提交
264
<div align="center"> <img src="pics/74dc31eb-6baa-47ea-ab1c-d27a0ca35093.png"/> </div><br>
C
CyC2018 已提交
265 266 267

广度优先搜索一层一层遍历,每一层得到的所有新节点,要用队列存储起来以备下一层遍历的时候再遍历。

C
CyC2018 已提交
268
而深度优先搜索在得到一个新节点时立即对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。
C
CyC2018 已提交
269

C
CyC2018 已提交
270
从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种  **可达性**  问题。
C
CyC2018 已提交
271

C
CyC2018 已提交
272
在程序实现 DFS 时需要考虑以下问题:
C
CyC2018 已提交
273

C
CyC2018 已提交
274 275
- 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。
- 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。
C
CyC2018 已提交
276

C
CyC2018 已提交
277
## 查找最大的连通面积
C
CyC2018 已提交
278

C
CyC2018 已提交
279
[695. Max Area of Island (Easy)](https://leetcode.com/problems/max-area-of-island/description/)
C
CyC2018 已提交
280 281 282

```html
[[0,0,1,0,0,0,0,1,0,0,0,0,0],
C
CyC2018 已提交
283 284 285 286 287 288 289
 [0,0,0,0,0,0,0,1,1,1,0,0,0],
 [0,1,1,0,1,0,0,0,0,0,0,0,0],
 [0,1,0,0,1,1,0,0,1,0,1,0,0],
 [0,1,0,0,1,1,0,0,1,1,1,0,0],
 [0,0,0,0,0,0,0,0,0,0,1,0,0],
 [0,0,0,0,0,0,0,1,1,1,0,0,0],
 [0,0,0,0,0,0,0,1,1,0,0,0,0]]
C
CyC2018 已提交
290 291 292
```

```java
C
CyC2018 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
private int m, n;
private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

public int maxAreaOfIsland(int[][] grid) {
    if (grid == null || grid.length == 0) {
        return 0;
    }
    m = grid.length;
    n = grid[0].length;
    int maxArea = 0;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            maxArea = Math.max(maxArea, dfs(grid, i, j));
        }
    }
    return maxArea;
C
CyC2018 已提交
309 310
}

C
CyC2018 已提交
311 312 313 314 315 316 317 318 319 320
private int dfs(int[][] grid, int r, int c) {
    if (r < 0 || r >= m || c < 0 || c >= n || grid[r][c] == 0) {
        return 0;
    }
    grid[r][c] = 0;
    int area = 1;
    for (int[] d : direction) {
        area += dfs(grid, r + d[0], c + d[1]);
    }
    return area;
C
CyC2018 已提交
321 322 323
}
```

C
CyC2018 已提交
324
## 矩阵中的连通分量数目
C
CyC2018 已提交
325

C
CyC2018 已提交
326
[200. Number of Islands (Medium)](https://leetcode.com/problems/number-of-islands/description/)
C
CyC2018 已提交
327 328 329 330 331 332 333 334

```html
Input:
11000
11000
00100
00011

C
CyC2018 已提交
335
Output: 3
C
CyC2018 已提交
336 337 338 339 340
```

可以将矩阵表示看成一张有向图。

```java
C
CyC2018 已提交
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
private int m, n;
private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

public int numIslands(char[][] grid) {
    if (grid == null || grid.length == 0) {
        return 0;
    }
    m = grid.length;
    n = grid[0].length;
    int islandsNum = 0;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (grid[i][j] != '0') {
                dfs(grid, i, j);
                islandsNum++;
            }
        }
    }
    return islandsNum;
C
CyC2018 已提交
360 361
}

C
CyC2018 已提交
362 363 364 365 366 367 368 369
private void dfs(char[][] grid, int i, int j) {
    if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == '0') {
        return;
    }
    grid[i][j] = '0';
    for (int[] d : direction) {
        dfs(grid, i + d[0], j + d[1]);
    }
C
CyC2018 已提交
370 371 372
}
```

C
CyC2018 已提交
373
## 好友关系的连通分量数目
C
CyC2018 已提交
374

C
CyC2018 已提交
375
[547. Friend Circles (Medium)](https://leetcode.com/problems/friend-circles/description/)
C
CyC2018 已提交
376 377 378 379

```html
Input:
[[1,1,0],
C
CyC2018 已提交
380 381
 [1,1,0],
 [0,0,1]]
C
CyC2018 已提交
382

C
CyC2018 已提交
383
Output: 2
C
CyC2018 已提交
384

C
CyC2018 已提交
385 386
Explanation:The 0th and 1st students are direct friends, so they are in a friend circle.
The 2nd student himself is in a friend circle. So return 2.
C
CyC2018 已提交
387 388
```

C
CyC2018 已提交
389
题目描述:好友关系可以看成是一个无向图,例如第 0 个人与第 1 个人是好友,那么 M[0][1] 和 M[1][0] 的值都为 1。
C
CyC2018 已提交
390 391

```java
C
CyC2018 已提交
392 393 394 395 396 397 398 399 400 401 402 403 404
private int n;

public int findCircleNum(int[][] M) {
    n = M.length;
    int circleNum = 0;
    boolean[] hasVisited = new boolean[n];
    for (int i = 0; i < n; i++) {
        if (!hasVisited[i]) {
            dfs(M, i, hasVisited);
            circleNum++;
        }
    }
    return circleNum;
C
CyC2018 已提交
405 406
}

C
CyC2018 已提交
407 408 409 410 411 412 413
private void dfs(int[][] M, int i, boolean[] hasVisited) {
    hasVisited[i] = true;
    for (int k = 0; k < n; k++) {
        if (M[i][k] == 1 && !hasVisited[k]) {
            dfs(M, k, hasVisited);
        }
    }
C
CyC2018 已提交
414 415 416
}
```

C
CyC2018 已提交
417
## 填充封闭区域
C
CyC2018 已提交
418

C
CyC2018 已提交
419
[130. Surrounded Regions (Medium)](https://leetcode.com/problems/surrounded-regions/description/)
C
CyC2018 已提交
420 421

```html
C
CyC2018 已提交
422 423 424 425 426 427 428 429 430 431 432
For example,
X X X X
X O O X
X X O X
X O X X

After running your function, the board should be:
X X X X
X X X X
X X X X
X O X X
C
CyC2018 已提交
433 434
```

C
CyC2018 已提交
435
题目描述:使被 'X' 包围的 'O' 转换为 'X'。
C
CyC2018 已提交
436 437 438 439

先填充最外侧,剩下的就是里侧了。

```java
C
CyC2018 已提交
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
private int m, n;

public void solve(char[][] board) {
    if (board == null || board.length == 0) {
        return;
    }

    m = board.length;
    n = board[0].length;

    for (int i = 0; i < m; i++) {
        dfs(board, i, 0);
        dfs(board, i, n - 1);
    }
    for (int i = 0; i < n; i++) {
        dfs(board, 0, i);
        dfs(board, m - 1, i);
    }

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (board[i][j] == 'T') {
                board[i][j] = 'O';
            } else if (board[i][j] == 'O') {
                board[i][j] = 'X';
            }
        }
    }
C
CyC2018 已提交
469 470
}

C
CyC2018 已提交
471 472 473 474 475 476 477 478
private void dfs(char[][] board, int r, int c) {
    if (r < 0 || r >= m || c < 0 || c >= n || board[r][c] != 'O') {
        return;
    }
    board[r][c] = 'T';
    for (int[] d : direction) {
        dfs(board, r + d[0], c + d[1]);
    }
C
CyC2018 已提交
479 480 481
}
```

C
CyC2018 已提交
482
## 能到达的太平洋和大西洋的区域
C
CyC2018 已提交
483

C
CyC2018 已提交
484
[417. Pacific Atlantic Water Flow (Medium)](https://leetcode.com/problems/pacific-atlantic-water-flow/description/)
C
CyC2018 已提交
485 486

```html
C
CyC2018 已提交
487
Given the following 5x5 matrix:
C
CyC2018 已提交
488

C
CyC2018 已提交
489 490 491 492 493 494 495
  Pacific ~   ~   ~   ~   ~
       ~  1   2   2   3  (5) *
       ~  3   2   3  (4) (4) *
       ~  2   4  (5)  3   1  *
       ~ (6) (7)  1   4   5  *
       ~ (5)  1   1   2   4  *
          *   *   *   *   * Atlantic
C
CyC2018 已提交
496 497

Return:
C
CyC2018 已提交
498
[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (positions with parentheses in above matrix).
C
CyC2018 已提交
499 500 501 502 503 504
```

左边和上边是太平洋,右边和下边是大西洋,内部的数字代表海拔,海拔高的地方的水能够流到低的地方,求解水能够流到太平洋和大西洋的所有位置。

```java

C
CyC2018 已提交
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
private int m, n;
private int[][] matrix;
private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

public List<int[]> pacificAtlantic(int[][] matrix) {
    List<int[]> ret = new ArrayList<>();
    if (matrix == null || matrix.length == 0) {
        return ret;
    }

    m = matrix.length;
    n = matrix[0].length;
    this.matrix = matrix;
    boolean[][] canReachP = new boolean[m][n];
    boolean[][] canReachA = new boolean[m][n];

    for (int i = 0; i < m; i++) {
        dfs(i, 0, canReachP);
        dfs(i, n - 1, canReachA);
    }
    for (int i = 0; i < n; i++) {
        dfs(0, i, canReachP);
        dfs(m - 1, i, canReachA);
    }

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (canReachP[i][j] && canReachA[i][j]) {
                ret.add(new int[]{i, j});
            }
        }
    }

    return ret;
C
CyC2018 已提交
539 540
}

C
CyC2018 已提交
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
private void dfs(int r, int c, boolean[][] canReach) {
    if (canReach[r][c]) {
        return;
    }
    canReach[r][c] = true;
    for (int[] d : direction) {
        int nextR = d[0] + r;
        int nextC = d[1] + c;
        if (nextR < 0 || nextR >= m || nextC < 0 || nextC >= n
                || matrix[r][c] > matrix[nextR][nextC]) {

            continue;
        }
        dfs(nextR, nextC, canReach);
    }
C
CyC2018 已提交
556 557 558
}
```

C
CyC2018 已提交
559
# Backtracking
C
CyC2018 已提交
560

C
CyC2018 已提交
561
Backtracking(回溯)属于 DFS。
C
CyC2018 已提交
562

C
CyC2018 已提交
563 564
- 普通 DFS 主要用在  **可达性问题** ,这种问题只需要执行到特点的位置然后返回即可。
- 而 Backtracking 主要用于求解  **排列组合**  问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串,这种问题在执行到特定的位置返回之后还会继续执行求解过程。
C
CyC2018 已提交
565

C
CyC2018 已提交
566
因为 Backtracking 不是立即就返回,而要继续求解,因此在程序实现时,需要注意对元素的标记问题:
C
CyC2018 已提交
567

C
CyC2018 已提交
568 569
- 在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;
- 但是在递归返回时,需要将元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。
C
CyC2018 已提交
570

C
CyC2018 已提交
571
## 数字键盘组合
C
CyC2018 已提交
572

C
CyC2018 已提交
573
[17. Letter Combinations of a Phone Number (Medium)](https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/)
C
CyC2018 已提交
574

C
CyC2018 已提交
575
<div align="center"> <img src="pics/9823768c-212b-4b1a-b69a-b3f59e07b977.jpg"/> </div><br>
C
CyC2018 已提交
576 577

```html
C
CyC2018 已提交
578 579
Input:Digit string "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
C
CyC2018 已提交
580 581 582
```

```java
C
CyC2018 已提交
583 584 585 586 587 588 589 590 591
private static final String[] KEYS = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};

public List<String> letterCombinations(String digits) {
    List<String> combinations = new ArrayList<>();
    if (digits == null || digits.length() == 0) {
        return combinations;
    }
    doCombination(new StringBuilder(), combinations, digits);
    return combinations;
C
CyC2018 已提交
592 593
}

C
CyC2018 已提交
594 595 596 597 598 599 600 601 602 603 604 605
private void doCombination(StringBuilder prefix, List<String> combinations, final String digits) {
    if (prefix.length() == digits.length()) {
        combinations.add(prefix.toString());
        return;
    }
    int curDigits = digits.charAt(prefix.length()) - '0';
    String letters = KEYS[curDigits];
    for (char c : letters.toCharArray()) {
        prefix.append(c);                         // 添加
        doCombination(prefix, combinations, digits);
        prefix.deleteCharAt(prefix.length() - 1); // 删除
    }
C
CyC2018 已提交
606 607 608
}
```

C
CyC2018 已提交
609
## IP 地址划分
C
CyC2018 已提交
610

C
CyC2018 已提交
611
[93. Restore IP Addresses(Medium)](https://leetcode.com/problems/restore-ip-addresses/description/)
C
CyC2018 已提交
612 613

```html
C
CyC2018 已提交
614 615
Given "25525511135",
return ["255.255.11.135", "255.255.111.35"].
C
CyC2018 已提交
616 617 618
```

```java
C
CyC2018 已提交
619 620 621 622 623
public List<String> restoreIpAddresses(String s) {
    List<String> addresses = new ArrayList<>();
    StringBuilder tempAddress = new StringBuilder();
    doRestore(0, tempAddress, addresses, s);
    return addresses;
C
CyC2018 已提交
624 625
}

C
CyC2018 已提交
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
private void doRestore(int k, StringBuilder tempAddress, List<String> addresses, String s) {
    if (k == 4 || s.length() == 0) {
        if (k == 4 && s.length() == 0) {
            addresses.add(tempAddress.toString());
        }
        return;
    }
    for (int i = 0; i < s.length() && i <= 2; i++) {
        if (i != 0 && s.charAt(0) == '0') {
            break;
        }
        String part = s.substring(0, i + 1);
        if (Integer.valueOf(part) <= 255) {
            if (tempAddress.length() != 0) {
                part = "." + part;
            }
            tempAddress.append(part);
            doRestore(k + 1, tempAddress, addresses, s.substring(i + 1));
            tempAddress.delete(tempAddress.length() - part.length(), tempAddress.length());
        }
    }
C
CyC2018 已提交
647 648 649
}
```

C
CyC2018 已提交
650
## 在矩阵中寻找字符串
C
CyC2018 已提交
651

C
CyC2018 已提交
652
[79. Word Search (Medium)](https://leetcode.com/problems/word-search/description/)
C
CyC2018 已提交
653 654

```html
C
CyC2018 已提交
655 656
For example,
Given board =
C
CyC2018 已提交
657
[
C
CyC2018 已提交
658 659 660
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
C
CyC2018 已提交
661
]
C
CyC2018 已提交
662 663 664
word = "ABCCED", -> returns true,
word = "SEE", -> returns true,
word = "ABCB", -> returns false.
C
CyC2018 已提交
665 666 667
```

```java
C
CyC2018 已提交
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
private final static int[][] direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
private int m;
private int n;

public boolean exist(char[][] board, String word) {
    if (word == null || word.length() == 0) {
        return true;
    }
    if (board == null || board.length == 0 || board[0].length == 0) {
        return false;
    }

    m = board.length;
    n = board[0].length;
    boolean[][] hasVisited = new boolean[m][n];

    for (int r = 0; r < m; r++) {
        for (int c = 0; c < n; c++) {
            if (backtracking(0, r, c, hasVisited, board, word)) {
                return true;
            }
        }
    }

    return false;
C
CyC2018 已提交
693 694
}

C
CyC2018 已提交
695 696 697 698 699 700
private boolean backtracking(int curLen, int r, int c, boolean[][] visited, final char[][] board, final String word) {
    if (curLen == word.length()) {
        return true;
    }
    if (r < 0 || r >= m || c < 0 || c >= n
            || board[r][c] != word.charAt(curLen) || visited[r][c]) {
C
CyC2018 已提交
701

C
CyC2018 已提交
702 703
        return false;
    }
C
CyC2018 已提交
704

C
CyC2018 已提交
705
    visited[r][c] = true;
C
CyC2018 已提交
706

C
CyC2018 已提交
707 708 709 710 711
    for (int[] d : direction) {
        if (backtracking(curLen + 1, r + d[0], c + d[1], visited, board, word)) {
            return true;
        }
    }
C
CyC2018 已提交
712

C
CyC2018 已提交
713
    visited[r][c] = false;
C
CyC2018 已提交
714

C
CyC2018 已提交
715
    return false;
C
CyC2018 已提交
716 717 718
}
```

C
CyC2018 已提交
719
## 输出二叉树中所有从根到叶子的路径
C
CyC2018 已提交
720

C
CyC2018 已提交
721
[257. Binary Tree Paths (Easy)](https://leetcode.com/problems/binary-tree-paths/description/)
C
CyC2018 已提交
722 723

```html
C
CyC2018 已提交
724 725 726 727 728
  1
 /  \
2    3
 \
  5
C
CyC2018 已提交
729 730 731
```

```html
C
CyC2018 已提交
732
["1->2->5", "1->3"]
C
CyC2018 已提交
733 734 735 736
```

```java

C
CyC2018 已提交
737 738 739 740 741 742 743 744
public List<String> binaryTreePaths(TreeNode root) {
    List<String> paths = new ArrayList<>();
    if (root == null) {
        return paths;
    }
    List<Integer> values = new ArrayList<>();
    backtracking(root, values, paths);
    return paths;
C
CyC2018 已提交
745 746
}

C
CyC2018 已提交
747 748 749 750 751 752 753 754 755 756 757 758
private void backtracking(TreeNode node, List<Integer> values, List<String> paths) {
    if (node == null) {
        return;
    }
    values.add(node.val);
    if (isLeaf(node)) {
        paths.add(buildPath(values));
    } else {
        backtracking(node.left, values, paths);
        backtracking(node.right, values, paths);
    }
    values.remove(values.size() - 1);
C
CyC2018 已提交
759 760
}

C
CyC2018 已提交
761 762
private boolean isLeaf(TreeNode node) {
    return node.left == null && node.right == null;
C
CyC2018 已提交
763 764
}

C
CyC2018 已提交
765 766 767 768 769 770 771 772 773
private String buildPath(List<Integer> values) {
    StringBuilder str = new StringBuilder();
    for (int i = 0; i < values.size(); i++) {
        str.append(values.get(i));
        if (i != values.size() - 1) {
            str.append("->");
        }
    }
    return str.toString();
C
CyC2018 已提交
774 775 776
}
```

C
CyC2018 已提交
777
## 排列
C
CyC2018 已提交
778

C
CyC2018 已提交
779
[46. Permutations (Medium)](https://leetcode.com/problems/permutations/description/)
C
CyC2018 已提交
780 781

```html
C
CyC2018 已提交
782
[1,2,3] have the following permutations:
C
CyC2018 已提交
783
[
C
CyC2018 已提交
784 785 786 787 788 789
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
C
CyC2018 已提交
790 791 792 793
]
```

```java
C
CyC2018 已提交
794 795 796 797 798 799
public List<List<Integer>> permute(int[] nums) {
    List<List<Integer>> permutes = new ArrayList<>();
    List<Integer> permuteList = new ArrayList<>();
    boolean[] hasVisited = new boolean[nums.length];
    backtracking(permuteList, permutes, hasVisited, nums);
    return permutes;
C
CyC2018 已提交
800 801
}

C
CyC2018 已提交
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
private void backtracking(List<Integer> permuteList, List<List<Integer>> permutes, boolean[] visited, final int[] nums) {
    if (permuteList.size() == nums.length) {
        permutes.add(new ArrayList<>(permuteList)); // 重新构造一个 List
        return;
    }
    for (int i = 0; i < visited.length; i++) {
        if (visited[i]) {
            continue;
        }
        visited[i] = true;
        permuteList.add(nums[i]);
        backtracking(permuteList, permutes, visited, nums);
        permuteList.remove(permuteList.size() - 1);
        visited[i] = false;
    }
C
CyC2018 已提交
817 818 819
}
```

C
CyC2018 已提交
820
## 含有相同元素求排列
C
CyC2018 已提交
821

C
CyC2018 已提交
822
[47. Permutations II (Medium)](https://leetcode.com/problems/permutations-ii/description/)
C
CyC2018 已提交
823 824

```html
C
CyC2018 已提交
825 826
[1,1,2] have the following unique permutations:
[[1,1,2], [1,2,1], [2,1,1]]
C
CyC2018 已提交
827 828 829 830
```

数组元素可能含有相同的元素,进行排列时就有可能出现重复的排列,要求重复的排列只返回一个。

C
CyC2018 已提交
831
在实现上,和 Permutations 不同的是要先排序,然后在添加一个元素时,判断这个元素是否等于前一个元素,如果等于,并且前一个元素还未访问,那么就跳过这个元素。
C
CyC2018 已提交
832 833

```java
C
CyC2018 已提交
834 835 836 837 838 839 840
public List<List<Integer>> permuteUnique(int[] nums) {
    List<List<Integer>> permutes = new ArrayList<>();
    List<Integer> permuteList = new ArrayList<>();
    Arrays.sort(nums);  // 排序
    boolean[] hasVisited = new boolean[nums.length];
    backtracking(permuteList, permutes, hasVisited, nums);
    return permutes;
C
CyC2018 已提交
841 842
}

C
CyC2018 已提交
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
private void backtracking(List<Integer> permuteList, List<List<Integer>> permutes, boolean[] visited, final int[] nums) {
    if (permuteList.size() == nums.length) {
        permutes.add(new ArrayList<>(permuteList));
        return;
    }

    for (int i = 0; i < visited.length; i++) {
        if (i != 0 && nums[i] == nums[i - 1] && !visited[i - 1]) {
            continue;  // 防止重复
        }
        if (visited[i]){
            continue;
        }
        visited[i] = true;
        permuteList.add(nums[i]);
        backtracking(permuteList, permutes, visited, nums);
        permuteList.remove(permuteList.size() - 1);
        visited[i] = false;
    }
C
CyC2018 已提交
862 863 864
}
```

C
CyC2018 已提交
865
## 组合
C
CyC2018 已提交
866

C
CyC2018 已提交
867
[77. Combinations (Medium)](https://leetcode.com/problems/combinations/description/)
C
CyC2018 已提交
868 869

```html
C
CyC2018 已提交
870
If n = 4 and k = 2, a solution is:
C
CyC2018 已提交
871
[
C
CyC2018 已提交
872 873 874 875 876 877
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
C
CyC2018 已提交
878 879 880 881
]
```

```java
C
CyC2018 已提交
882 883 884 885 886
public List<List<Integer>> combine(int n, int k) {
    List<List<Integer>> combinations = new ArrayList<>();
    List<Integer> combineList = new ArrayList<>();
    backtracking(combineList, combinations, 1, k, n);
    return combinations;
C
CyC2018 已提交
887 888
}

C
CyC2018 已提交
889 890 891 892 893 894 895 896 897 898
private void backtracking(List<Integer> combineList, List<List<Integer>> combinations, int start, int k, final int n) {
    if (k == 0) {
        combinations.add(new ArrayList<>(combineList));
        return;
    }
    for (int i = start; i <= n - k + 1; i++) {  // 剪枝
        combineList.add(i);
        backtracking(combineList, combinations, i + 1, k - 1, n);
        combineList.remove(combineList.size() - 1);
    }
C
CyC2018 已提交
899 900 901
}
```

C
CyC2018 已提交
902
## 组合求和
C
CyC2018 已提交
903

C
CyC2018 已提交
904
[39. Combination Sum (Medium)](https://leetcode.com/problems/combination-sum/description/)
C
CyC2018 已提交
905 906

```html
C
CyC2018 已提交
907 908 909
given candidate set [2, 3, 6, 7] and target 7,
A solution set is:
[[7],[2, 2, 3]]
C
CyC2018 已提交
910 911 912
```

```java
C
CyC2018 已提交
913 914 915 916
public List<List<Integer>> combinationSum(int[] candidates, int target) {
    List<List<Integer>> combinations = new ArrayList<>();
    backtracking(new ArrayList<>(), combinations, 0, target, candidates);
    return combinations;
C
CyC2018 已提交
917 918
}

C
CyC2018 已提交
919 920 921 922 923 924 925 926 927 928 929 930 931 932
private void backtracking(List<Integer> tempCombination, List<List<Integer>> combinations,
                          int start, int target, final int[] candidates) {

    if (target == 0) {
        combinations.add(new ArrayList<>(tempCombination));
        return;
    }
    for (int i = start; i < candidates.length; i++) {
        if (candidates[i] <= target) {
            tempCombination.add(candidates[i]);
            backtracking(tempCombination, combinations, i, target - candidates[i], candidates);
            tempCombination.remove(tempCombination.size() - 1);
        }
    }
C
CyC2018 已提交
933 934 935
}
```

C
CyC2018 已提交
936
## 含有相同元素的求组合求和
C
CyC2018 已提交
937

C
CyC2018 已提交
938
[40. Combination Sum II (Medium)](https://leetcode.com/problems/combination-sum-ii/description/)
C
CyC2018 已提交
939 940

```html
C
CyC2018 已提交
941 942
For example, given candidate set [10, 1, 2, 7, 6, 1, 5] and target 8,
A solution set is:
C
CyC2018 已提交
943
[
C
CyC2018 已提交
944 945 946 947
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
C
CyC2018 已提交
948 949 950 951
]
```

```java
C
CyC2018 已提交
952 953 954 955 956
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
    List<List<Integer>> combinations = new ArrayList<>();
    Arrays.sort(candidates);
    backtracking(new ArrayList<>(), combinations, new boolean[candidates.length], 0, target, candidates);
    return combinations;
C
CyC2018 已提交
957 958
}

C
CyC2018 已提交
959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977
private void backtracking(List<Integer> tempCombination, List<List<Integer>> combinations,
                          boolean[] hasVisited, int start, int target, final int[] candidates) {

    if (target == 0) {
        combinations.add(new ArrayList<>(tempCombination));
        return;
    }
    for (int i = start; i < candidates.length; i++) {
        if (i != 0 && candidates[i] == candidates[i - 1] && !hasVisited[i - 1]) {
            continue;
        }
        if (candidates[i] <= target) {
            tempCombination.add(candidates[i]);
            hasVisited[i] = true;
            backtracking(tempCombination, combinations, hasVisited, i + 1, target - candidates[i], candidates);
            hasVisited[i] = false;
            tempCombination.remove(tempCombination.size() - 1);
        }
    }
C
CyC2018 已提交
978 979 980
}
```

C
CyC2018 已提交
981
## 1-9 数字的组合求和
C
CyC2018 已提交
982

C
CyC2018 已提交
983
[216. Combination Sum III (Medium)](https://leetcode.com/problems/combination-sum-iii/description/)
C
CyC2018 已提交
984 985

```html
C
CyC2018 已提交
986
Input: k = 3, n = 9
C
CyC2018 已提交
987 988 989

Output:

C
CyC2018 已提交
990
[[1,2,6], [1,3,5], [2,3,4]]
C
CyC2018 已提交
991 992
```

C
CyC2018 已提交
993
从 1-9 数字中选出 k 个数不重复的数,使得它们的和为 n。
C
CyC2018 已提交
994 995

```java
C
CyC2018 已提交
996 997 998 999 1000
public List<List<Integer>> combinationSum3(int k, int n) {
    List<List<Integer>> combinations = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    backtracking(k, n, 1, path, combinations);
    return combinations;
C
CyC2018 已提交
1001 1002
}

C
CyC2018 已提交
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
private void backtracking(int k, int n, int start,
                          List<Integer> tempCombination, List<List<Integer>> combinations) {

    if (k == 0 && n == 0) {
        combinations.add(new ArrayList<>(tempCombination));
        return;
    }
    if (k == 0 || n == 0) {
        return;
    }
    for (int i = start; i <= 9; i++) {
        tempCombination.add(i);
        backtracking(k - 1, n - i, i + 1, tempCombination, combinations);
        tempCombination.remove(tempCombination.size() - 1);
    }
C
CyC2018 已提交
1018 1019 1020
}
```

C
CyC2018 已提交
1021
## 子集
C
CyC2018 已提交
1022

C
CyC2018 已提交
1023
[78. Subsets (Medium)](https://leetcode.com/problems/subsets/description/)
C
CyC2018 已提交
1024

C
CyC2018 已提交
1025
找出集合的所有子集,子集不能重复,[1, 2] 和 [2, 1] 这种子集算重复
C
CyC2018 已提交
1026 1027

```java
C
CyC2018 已提交
1028 1029 1030 1031 1032 1033 1034
public List<List<Integer>> subsets(int[] nums) {
    List<List<Integer>> subsets = new ArrayList<>();
    List<Integer> tempSubset = new ArrayList<>();
    for (int size = 0; size <= nums.length; size++) {
        backtracking(0, tempSubset, subsets, size, nums); // 不同的子集大小
    }
    return subsets;
C
CyC2018 已提交
1035 1036
}

C
CyC2018 已提交
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
private void backtracking(int start, List<Integer> tempSubset, List<List<Integer>> subsets,
                          final int size, final int[] nums) {

    if (tempSubset.size() == size) {
        subsets.add(new ArrayList<>(tempSubset));
        return;
    }
    for (int i = start; i < nums.length; i++) {
        tempSubset.add(nums[i]);
        backtracking(i + 1, tempSubset, subsets, size, nums);
        tempSubset.remove(tempSubset.size() - 1);
    }
C
CyC2018 已提交
1049 1050 1051
}
```

C
CyC2018 已提交
1052
## 含有相同元素求子集
C
CyC2018 已提交
1053

C
CyC2018 已提交
1054
[90. Subsets II (Medium)](https://leetcode.com/problems/subsets-ii/description/)
C
CyC2018 已提交
1055 1056

```html
C
CyC2018 已提交
1057 1058
For example,
If nums = [1,2,2], a solution is:
C
CyC2018 已提交
1059 1060

[
C
CyC2018 已提交
1061 1062 1063 1064 1065 1066
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
C
CyC2018 已提交
1067 1068 1069 1070
]
```

```java
C
CyC2018 已提交
1071 1072 1073 1074 1075 1076 1077 1078 1079
public List<List<Integer>> subsetsWithDup(int[] nums) {
    Arrays.sort(nums);
    List<List<Integer>> subsets = new ArrayList<>();
    List<Integer> tempSubset = new ArrayList<>();
    boolean[] hasVisited = new boolean[nums.length];
    for (int size = 0; size <= nums.length; size++) {
        backtracking(0, tempSubset, subsets, hasVisited, size, nums); // 不同的子集大小
    }
    return subsets;
C
CyC2018 已提交
1080 1081
}

C
CyC2018 已提交
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098
private void backtracking(int start, List<Integer> tempSubset, List<List<Integer>> subsets, boolean[] hasVisited,
                          final int size, final int[] nums) {

    if (tempSubset.size() == size) {
        subsets.add(new ArrayList<>(tempSubset));
        return;
    }
    for (int i = start; i < nums.length; i++) {
        if (i != 0 && nums[i] == nums[i - 1] && !hasVisited[i - 1]) {
            continue;
        }
        tempSubset.add(nums[i]);
        hasVisited[i] = true;
        backtracking(i + 1, tempSubset, subsets, hasVisited, size, nums);
        hasVisited[i] = false;
        tempSubset.remove(tempSubset.size() - 1);
    }
C
CyC2018 已提交
1099 1100 1101
}
```

C
CyC2018 已提交
1102
## 分割字符串使得每个部分都是回文数
C
CyC2018 已提交
1103

C
CyC2018 已提交
1104
[131. Palindrome Partitioning (Medium)](https://leetcode.com/problems/palindrome-partitioning/description/)
C
CyC2018 已提交
1105 1106

```html
C
CyC2018 已提交
1107
For example, given s = "aab",
C
CyC2018 已提交
1108 1109 1110
Return

[
C
CyC2018 已提交
1111 1112
  ["aa","b"],
  ["a","a","b"]
C
CyC2018 已提交
1113 1114 1115 1116
]
```

```java
C
CyC2018 已提交
1117 1118 1119 1120 1121
public List<List<String>> partition(String s) {
    List<List<String>> partitions = new ArrayList<>();
    List<String> tempPartition = new ArrayList<>();
    doPartition(s, partitions, tempPartition);
    return partitions;
C
CyC2018 已提交
1122 1123
}

C
CyC2018 已提交
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135
private void doPartition(String s, List<List<String>> partitions, List<String> tempPartition) {
    if (s.length() == 0) {
        partitions.add(new ArrayList<>(tempPartition));
        return;
    }
    for (int i = 0; i < s.length(); i++) {
        if (isPalindrome(s, 0, i)) {
            tempPartition.add(s.substring(0, i + 1));
            doPartition(s.substring(i + 1), partitions, tempPartition);
            tempPartition.remove(tempPartition.size() - 1);
        }
    }
C
CyC2018 已提交
1136 1137
}

C
CyC2018 已提交
1138 1139 1140 1141 1142 1143 1144
private boolean isPalindrome(String s, int begin, int end) {
    while (begin < end) {
        if (s.charAt(begin++) != s.charAt(end--)) {
            return false;
        }
    }
    return true;
C
CyC2018 已提交
1145 1146 1147
}
```

C
CyC2018 已提交
1148
## 数独
C
CyC2018 已提交
1149

C
CyC2018 已提交
1150
[37. Sudoku Solver (Hard)](https://leetcode.com/problems/sudoku-solver/description/)
C
CyC2018 已提交
1151

C
CyC2018 已提交
1152
<div align="center"> <img src="pics/0e8fdc96-83c1-4798-9abe-45fc91d70b9d.png"/> </div><br>
C
CyC2018 已提交
1153 1154

```java
C
CyC2018 已提交
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
private boolean[][] rowsUsed = new boolean[9][10];
private boolean[][] colsUsed = new boolean[9][10];
private boolean[][] cubesUsed = new boolean[9][10];
private char[][] board;

public void solveSudoku(char[][] board) {
    this.board = board;
    for (int i = 0; i < 9; i++)
        for (int j = 0; j < 9; j++) {
            if (board[i][j] == '.') {
                continue;
            }
            int num = board[i][j] - '0';
            rowsUsed[i][num] = true;
            colsUsed[j][num] = true;
            cubesUsed[cubeNum(i, j)][num] = true;
        }
        backtracking(0, 0);
C
CyC2018 已提交
1173 1174
}

C
CyC2018 已提交
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195
private boolean backtracking(int row, int col) {
    while (row < 9 && board[row][col] != '.') {
        row = col == 8 ? row + 1 : row;
        col = col == 8 ? 0 : col + 1;
    }
    if (row == 9) {
        return true;
    }
    for (int num = 1; num <= 9; num++) {
        if (rowsUsed[row][num] || colsUsed[col][num] || cubesUsed[cubeNum(row, col)][num]) {
            continue;
        }
        rowsUsed[row][num] = colsUsed[col][num] = cubesUsed[cubeNum(row, col)][num] = true;
        board[row][col] = (char) (num + '0');
        if (backtracking(row, col)) {
            return true;
        }
        board[row][col] = '.';
        rowsUsed[row][num] = colsUsed[col][num] = cubesUsed[cubeNum(row, col)][num] = false;
    }
    return false;
C
CyC2018 已提交
1196 1197
}

C
CyC2018 已提交
1198 1199 1200 1201
private int cubeNum(int i, int j) {
    int r = i / 3;
    int c = j / 3;
    return r * 3 + c;
C
CyC2018 已提交
1202 1203 1204
}
```

C
CyC2018 已提交
1205
## N 皇后
C
CyC2018 已提交
1206

C
CyC2018 已提交
1207
[51. N-Queens (Hard)](https://leetcode.com/problems/n-queens/description/)
C
CyC2018 已提交
1208

C
CyC2018 已提交
1209
<div align="center"> <img src="pics/067b310c-6877-40fe-9dcf-10654e737485.jpg"/> </div><br>
C
CyC2018 已提交
1210

C
CyC2018 已提交
1211
在 n\*n 的矩阵中摆放 n 个皇后,并且每个皇后不能在同一行,同一列,同一对角线上,求所有的 n 皇后的解。
C
CyC2018 已提交
1212

C
CyC2018 已提交
1213
一行一行地摆放,在确定一行中的那个皇后应该摆在哪一列时,需要用三个标记数组来确定某一列是否合法,这三个标记数组分别为:列标记数组、45 度对角线标记数组和 135 度对角线标记数组。
C
CyC2018 已提交
1214

C
CyC2018 已提交
1215
45 度对角线标记数组的长度为 2 \* n - 1,通过下图可以明确 (r, c) 的位置所在的数组下标为 r + c。
C
CyC2018 已提交
1216

C
CyC2018 已提交
1217
<div align="center"> <img src="pics/6646db4a-7f43-45e4-96ff-0891a57a9ade.jpg"/> </div><br>
C
CyC2018 已提交
1218

C
CyC2018 已提交
1219
135 度对角线标记数组的长度也是 2 \* n - 1,(r, c) 的位置所在的数组下标为 n - 1 - (r - c)。
C
CyC2018 已提交
1220

C
CyC2018 已提交
1221
<div align="center"> <img src="pics/f1ff65ed-bbc2-4b92-8a94-7c5c0874da0f.jpg"/> </div><br>
C
CyC2018 已提交
1222 1223

```java
C
CyC2018 已提交
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242
private List<List<String>> solutions;
private char[][] nQueens;
private boolean[] colUsed;
private boolean[] diagonals45Used;
private boolean[] diagonals135Used;
private int n;

public List<List<String>> solveNQueens(int n) {
    solutions = new ArrayList<>();
    nQueens = new char[n][n];
    for (int i = 0; i < n; i++) {
        Arrays.fill(nQueens[i], '.');
    }
    colUsed = new boolean[n];
    diagonals45Used = new boolean[2 * n - 1];
    diagonals135Used = new boolean[2 * n - 1];
    this.n = n;
    backtracking(0);
    return solutions;
C
CyC2018 已提交
1243 1244
}

C
CyC2018 已提交
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266
private void backtracking(int row) {
    if (row == n) {
        List<String> list = new ArrayList<>();
        for (char[] chars : nQueens) {
            list.add(new String(chars));
        }
        solutions.add(list);
        return;
    }

    for (int col = 0; col < n; col++) {
        int diagonals45Idx = row + col;
        int diagonals135Idx = n - 1 - (row - col);
        if (colUsed[col] || diagonals45Used[diagonals45Idx] || diagonals135Used[diagonals135Idx]) {
            continue;
        }
        nQueens[row][col] = 'Q';
        colUsed[col] = diagonals45Used[diagonals45Idx] = diagonals135Used[diagonals135Idx] = true;
        backtracking(row + 1);
        colUsed[col] = diagonals45Used[diagonals45Idx] = diagonals135Used[diagonals135Idx] = false;
        nQueens[row][col] = '.';
    }
C
CyC2018 已提交
1267 1268
}
```
C
CyC2018 已提交
1269
</br></br><div align="center">欢迎关注公众号,获取最新文章!</div></br>
C
CyC2018 已提交
1270
<div align="center"><img width="150px" src="https://cyc-1256109796.cos.ap-guangzhou.myqcloud.com/%E5%85%AC%E4%BC%97%E5%8F%B7.jpg"></img></div>