基礎部分
BFS
1091. 二進制矩陣中的最短路徑
中等
在一個 N × N 的方形網格中,每個單元格有兩種狀態:空(0)或者阻塞(1)。
一條從左上角到右下角、長度為 k
的暢通路徑,由滿足下述條件的單元格 C_1, C_2, ..., C_k
組成:
- 相鄰單元格
C_i
和C_{i+1}
在八個方向之一上連通(此時,C_i
和C_{i+1}
不同且共享邊或角) C_1
位於(0, 0)
(即,值為grid[0][0]
)C_k
位於(N-1, N-1)
(即,值為grid[N-1][N-1]
)- 如果
C_i
位於(r, c)
,則grid[r][c]
為空(即,grid[r][c] == 0
)
返回這條從左上角到右下角的最短暢通路徑的長度。如果不存在這樣的路徑,返回 -1 。
示例 1:
輸入:[[0,1],[1,0]]
輸出:2
示例 2:
輸入:[[0,0,0],[1,1,0],[1,1,0]]
輸出:4
提示:
1 <= grid.length == grid[0].length <= 100
grid[i][j]
為0
或1
import java.util.ArrayDeque;
import java.util.Queue;
class Solution {
public int shortestPathBinaryMatrix(int[][] grid) {
if (grid[0][0] == 1 || grid[grid.length-1][grid[0].length-1] == 1) return -1;
Queue<int[]> queue = new ArrayDeque<>();
queue.add(new int[]{0,0});
grid[0][0] = 1;
int res = 0;
while (!queue.isEmpty()){
res++;
int len = queue.size();
while (len-- > 0){
int[] zuobiao = queue.poll();
int i = zuobiao[0];
int j = zuobiao[1];
if(i==grid.length-1 && j==grid[0].length-1) return res;
if (i > 0 && grid[i-1][j] == 0){ //上
queue.add(new int[]{i-1,j});
grid[i-1][j] = 1;
}
if (i > 0 && j > 0 && grid[i-1][j-1] == 0){ //左上
queue.add(new int[]{i-1,j-1});
grid[i-1][j-1] = 1;
}
if (i > 0 && j < grid[0].length-1 && grid[i-1][j+1] == 0){ //右上
queue.add(new int[]{i-1,j+1});
grid[i-1][j+1] = 1;
}
if (i < grid.length-1 && grid[i+1][j] == 0){ //下
queue.add(new int[]{i+1,j});
grid[i+1][j] = 1;
}
if (i < grid.length-1 && j > 0 && grid[i+1][j-1] == 0){ //左下
queue.add(new int[]{i+1,j-1});
grid[i+1][j-1] = 1;
}
if (i < grid.length-1 && j < grid[0].length-1 && grid[i+1][j+1] == 0){ //右下
queue.add(new int[]{i+1,j+1});
grid[i+1][j+1] = 1;
}
if (j > 0 && grid[i][j-1] == 0){ //左
queue.add(new int[]{i,j-1});
grid[i][j-1] = 1;
}
if (j < grid[0].length-1 && grid[i][j+1] == 0){ //右
queue.add(new int[]{i,j+1});
grid[i][j+1] = 1;
}
}
}
return -1;
}
}
279. 完全平方數
中等
給定正整數 n,找到若干個完全平方數(比如 1, 4, 9, 16, ...
)使得它們的和等於 n。你需要讓組成和的完全平方數的個數最少。
示例 1:
輸入: n = 12
輸出: 3
解釋: 12 = 4 + 4 + 4.
示例 2:
輸入: n = 13
輸出: 2
解釋: 13 = 4 + 9.
class Solution { //很意外:12 ms, 在所有 Java 提交中擊敗了94.28%的用戶
public static int numSquares(int n) {
List<Integer> arr = new ArrayList<>();
Queue<Integer> queue = new ArrayDeque<>();
for (int i = 1; ; i++){
long a = i*i;
if (a == n) return 1;
if (a > n) break;
arr.add((int) a);
queue.add((int) a);
}
if (ij(arr,n)) return 2;
int res = 0;
while (!queue.isEmpty()){
res++;
int size = queue.size();
while (size-- > 0){
int num = queue.poll();
if (num == n) return res;
if (ij(arr,n-num)) return res+2;
else {
for (Integer i : arr) {
queue.add(i+num);
}
}
}
}
return 1;
}
private static boolean ij(List<Integer> arr, int n) {
//雙指針查找,答案基本不會大於4,所以這樣省不少時間
int i = 0;
int j = arr.size() - 1;
while (i <= j){
int x = arr.get(i);
int y = arr.get(j);
if (x + y == n) return true;
if (x + y > n) j--;
else i++;
}
return false;
}
}
127. 單詞接龍
中等
給定兩個單詞(beginWord 和 endWord)和一個字典,找到從 beginWord 到 endWord 的最短轉換序列的長度。轉換需遵循如下規則:
- 每次轉換只能改變一個字母。
- 轉換過程中的中間單詞必須是字典中的單詞。
說明:
- 如果不存在這樣的轉換序列,返回 0。
- 所有單詞具有相同的長度。
- 所有單詞只由小寫字母組成。
- 字典中不存在重復的單詞。
- 你可以假設 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:
輸入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
輸出: 5
解釋: 一個最短轉換序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
返回它的長度 5。
示例 2:
輸入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
輸出: 0
解釋: endWord "cog" 不在字典中,所以無法進行轉換。
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
if (!wordList.contains(endWord)) return 0;
Queue<String> queue = new ArrayDeque<>();
queue.add(beginWord);
int res = 1;
while (!queue.isEmpty()){
int size = queue.size();
res++;
while (size-- > 0){
String str = queue.poll();
if (canTran(str,endWord)) return res;
for (int i = wordList.size()-1; i >= 0; i--) {
if (canTran(str,wordList.get(i))){
queue.add(wordList.get(i));
wordList.remove(i);
}
}
}
}
return 0;
}
private boolean canTran(String s1,String s2){
boolean can = true;
for (int i = 0; i < s1.length(); i++) {
if (s1.charAt(i)!=s2.charAt(i)){
if (can) can = false;
else return false;
}
}
return true;
}
}
DFS
695. 島嶼的最大面積
中等
給定一個包含了一些 0
和 1
的非空二維數組 grid
。
一個 島嶼 是由一些相鄰的 1
(代表土地) 構成的組合,這里的「相鄰」要求兩個 1
必須在水平或者豎直方向上相鄰。你可以假設 grid
的四個邊緣都被 0
(代表水)包圍着。
找到給定的二維數組中最大的島嶼面積。(如果沒有島嶼,則返回面積為 0
。)
示例 1:
[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[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]]
對於上面這個給定矩陣應返回 6
。注意答案不應該是 11
,因為島嶼只能包含水平或垂直的四個方向的 1
。
示例 2:
[[0,0,0,0,0,0,0,0]]
對於上面這個給定的矩陣, 返回 0
。
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int row = grid.length;
int column = grid[0].length;
int res = 0;
for (int i = 0; i < row; i++) {
for (int j = 0; j < column; j++) {
if (grid[i][j] == 1) {
Stack<int[]> stack = new Stack<>();
stack.push(new int[]{i,j});
int S = 0;
while (!stack.isEmpty()){
int[] point = stack.pop();
int x = point[0];
int y = point[1];
if (grid[x][y] == 1) {
S++;
grid[x][y] = 0;
}else continue;
if (x > 0 && grid[x-1][y] == 1) stack.push(new int[]{x-1,y});
if (x < row-1 && grid[x+1][y] == 1) stack.push(new int[]{x+1,y});
if (y > 0 && grid[x][y-1] == 1) stack.push(new int[]{x,y-1});
if (y < column-1 && grid[x][y+1] == 1) stack.push(new int[]{x,y+1});
}
res = res > S ? res : S;
}
}
}
return res;
}
}
200. 島嶼數量
中等
給你一個由 '1'
(陸地)和 '0'
(水)組成的的二維網格,請你計算網格中島嶼的數量。
島嶼總是被水包圍,並且每座島嶼只能由水平方向或豎直方向上相鄰的陸地連接形成。
此外,你可以假設該網格的四條邊均被水包圍。
示例 1:
輸入:
[
['1','1','1','1','0'],
['1','1','0','1','0'],
['1','1','0','0','0'],
['0','0','0','0','0']
]
輸出: 1
示例 2:
輸入:
[
['1','1','0','0','0'],
['1','1','0','0','0'],
['0','0','1','0','0'],
['0','0','0','1','1']
]
輸出: 3
解釋: 每座島嶼只能由水平和/或豎直方向上相鄰的陸地連接而成。
class Solution {
public int numIslands(char[][] grid) {
int res = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == '1'){
res++;
dfs(i,j,grid);
}
}
}
return res;
}
private void dfs(int i, int j, char[][] grid) {
grid[i][j] = '0';
if (i > 0 && grid[i-1][j] == '1') dfs(i-1,j,grid);
if (i < grid.length-1 && grid[i+1][j] == '1') dfs(i+1,j,grid);
if (j > 0 && grid[i][j-1] == '1') dfs(i,j-1,grid);
if (j < grid[0].length-1 && grid[i][j+1] == '1') dfs(i,j+1,grid);
}
}
547. 朋友圈
中等
班上有 N 名學生。其中有些人是朋友,有些則不是。他們的友誼具有是傳遞性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我們可以認為 A 也是 C 的朋友。所謂的朋友圈,是指所有朋友的集合。
給定一個 N * N 的矩陣 M,表示班級中學生之間的朋友關系。如果M[i][j] = 1,表示已知第 i 個和 j 個學生互為朋友關系,否則為不知道。你必須輸出所有學生中的已知的朋友圈總數。
示例 1:
輸入:
[[1,1,0],
[1,1,0],
[0,0,1]]
輸出: 2
說明:已知學生0和學生1互為朋友,他們在一個朋友圈。
第2個學生自己在一個朋友圈。所以返回2。
示例 2:
輸入:
[[1,1,0],
[1,1,1],
[0,1,1]]
輸出: 1
說明:已知學生0和學生1互為朋友,學生1和學生2互為朋友,所以學生0和學生2也是朋友,所以他們三個在一個朋友圈,返回1。
注意:
- N 在[1,200]的范圍內。
- 對於所有學生,有M[i][i] = 1。
- 如果有M[i][j] = 1,則有M[j][i] = 1。
class Solution {
public int findCircleNum(int[][] M) {
int n = M.length;
int res = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (M[i][j] == 1){
M[i][j] = 0;
M[j][i] = 0;
res++;
dfs(j,M);
}
}
}
return res;
}
private void dfs(int j, int[][] M) {
for (int i = 0; i < M.length; i++) {
if (M[i][j] == 1) {
M[i][j] = 0;
M[j][i] = 0;
dfs(i,M);
}
}
}
}
130. 被圍繞的區域
中等
給定一個二維的矩陣,包含 'X'
和 'O'
(字母 O)。
找到所有被 'X'
圍繞的區域,並將這些區域里所有的 'O'
用 'X'
填充。
示例:
X X X X
X O O X
X X O X
X O X X
運行你的函數后,矩陣變為:
X X X X
X X X X
X X X X
X O X X
解釋:
被圍繞的區間不會存在於邊界上,換句話說,任何邊界上的 'O'
都不會被填充為 'X'
。 任何不在邊界上,或不與邊界上的 'O'
相連的 'O'
最終都會被填充為 'X'
。如果兩個元素在水平或垂直方向相鄰,則稱它們是“相連”的。
class Solution {
List<int[]> list = new ArrayList<>();
boolean[][] visited;
public void solve(char[][] board) {
if (board.length == 0) return;
visited = new boolean[board.length][board[0].length];
for (int i= 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] == 'X' || visited[i][j]) continue;
dfs(i,j,board);
boolean flag = true;
for (int[] ints : list) {
if (ints[0] == 0 || ints[0] == board.length-1 || ints[1] == 0 || ints[1] == board[0].length-1){
flag = false;
break;
}
}
if (flag){
for (int[] ints : list) {
board[ints[0]][ints[1]] = 'X';
}
}
list.clear();
}
}
}
private void dfs(int i, int j, char[][] board) {
if (visited[i][j]) return;
visited[i][j] = true;
list.add(new int[]{i,j});
if (i > 0 && board[i-1][j] == 'O') dfs(i-1,j,board);
if (i < board.length-1 && board[i+1][j] == 'O') dfs(i+1,j,board);
if (j > 0 && board[i][j-1] == 'O') dfs(i,j-1,board);
if (j < board[0].length-1 && board[i][j+1] == 'O') dfs(i,j+1,board);
}
}
417. 太平洋大西洋水流問題
中等
給定一個 m x n
的非負整數矩陣來表示一片大陸上各個單元格的高度。“太平洋”處於大陸的左邊界和上邊界,而“大西洋”處於大陸的右邊界和下邊界。
規定水流只能按照上、下、左、右四個方向流動,且只能從高到低或者在同等高度上流動。
請找出那些水流既可以流動到“太平洋”,又能流動到“大西洋”的陸地單元的坐標。
提示:
- 輸出坐標的順序不重要
- m 和 n 都小於150
示例:
給定下面的 5x5 矩陣:
太平洋 ~ ~ ~ ~ ~
~ 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 *
* * * * * 大西洋
返回:
[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (上圖中帶括號的單元).
public class Solution {
private static int[][] dires = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
private int m, n;
private int[][] matrix;
public List<List<Integer>> pacificAtlantic(int[][] matrix) {
List<List<Integer>> res = new ArrayList<>();
m = matrix.length;
if (m == 0) return res;
n = matrix[0].length;
if (n == 0) return res;
this.matrix = matrix;
boolean[][] canReachP = new boolean[m][n];
boolean[][] canReachA = new boolean[m][n];
for (int i = 0; i < n; i++) {
dfs(0, i, canReachP);
dfs(m - 1, i, canReachA);
}
for (int i = 0; i < m; i++) {
dfs(i, 0, canReachP);
dfs(i, n - 1, canReachA);
}
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(canReachA[i][j] && canReachP[i][j]){
List<Integer> temp = new ArrayList<>();
temp.add(i);
temp.add(j);
res.add(temp);
}
}
}
return res;
}
//換一種思路,從邊界往里面走,只能走到比自己更高或者等高的地方。
//邊界能走到的地方,就是能流入對應海洋的地方。
private void dfs(int x, int y, boolean[][] canReach) {
canReach[x][y] = true;
for (int i = 0; i < 4; i++) {
int newX = x + dires[i][0];
int newY = y + dires[i][1];
if (isIn(newX, newY) && matrix[x][y] <= matrix[newX][newY] && !canReach[newX][newY]) {
dfs(newX, newY, canReach);
}
}
}
private boolean isIn(int x, int y) {
return x >= 0 && x < m && y >= 0 && y < n;
}
}
回溯法
17. 電話號碼的字母組合
中等
給定一個僅包含數字 2-9
的字符串,返回所有它能表示的字母組合。
給出數字到字母的映射如下(與電話按鍵相同)。注意 1 不對應任何字母。
示例:
輸入:"23"
輸出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
說明:
盡管上面的答案是按字典序排列的,但是你可以任意選擇答案輸出的順序。
class Solution {
String[] strings = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
List<String> result = new ArrayList<>();
public List<String> letterCombinations(String digits) {
char[] ds = digits.toCharArray();
if (ds.length == 0) return result;
StringBuilder sb = new StringBuilder();
backtrack(sb,ds,0);
return result;
}
private void backtrack(StringBuilder sb, char[] ds, int index) {
if (sb.length() == ds.length){
result.add(sb.toString());return;
}
char[] chs = strings[ds[index] - '0'].toCharArray();
for (int i = 0; i < chs.length; i++) {
sb.append(chs[i]);
backtrack(sb,ds,index+1);
sb.deleteCharAt(sb.length()-1);
}
}
}
93. 復原IP地址
中等
給定一個只包含數字的字符串,復原它並返回所有可能的 IP 地址格式。
有效的 IP 地址正好由四個整數(每個整數位於 0 到 255 之間組成),整數之間用 '.'
分隔。
示例:
輸入: "25525511135"
輸出: ["255.255.11.135", "255.255.111.35"]
class Solution {
List<String> result = new ArrayList<>();
int len; //一直能復用
public List<String> restoreIpAddresses(String s) {
len = s.length();
if (len <= 12) helper(s,0,"",0);
return result;
}
private void helper(String ip, int index, String restore, int count) {
if (count == 4 && index == len) result.add(restore);
if (count >= 4) return;
for (int i = 1; i < 4; i++) {
if (index + i > len) break;
String s = ip.substring(index,index+i);
if (i > 1 && s.charAt(0) == '0' || Integer.parseInt(s) > 255)
break; //i即s的長度
helper(ip, index+i, restore + s + (count < 3 ? "." : "" ), count+1);
}
}
}
79. 單詞搜索
中等
給定一個二維網格和一個單詞,找出該單詞是否存在於網格中。
單詞必須按照字母順序,通過相鄰的單元格內的字母構成,其中“相鄰”單元格是那些水平相鄰或垂直相鄰的單元格。同一個單元格內的字母不允許被重復使用。
示例:
board =
[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]
給定 word = "ABCCED", 返回 true
給定 word = "SEE", 返回 true
給定 word = "ABCB", 返回 false
提示:
board
和word
中只包含大寫和小寫英文字母。1 <= board.length <= 200
1 <= board[i].length <= 200
1 <= word.length <= 10^3
class Solution {
int m;
int n;
int[][] directions = {{-1,0},{1,0},{0,-1},{0,1}};
boolean res = false;
public boolean exist(char[][] board, String word) {
this.m = board.length;
this.n = board[0].length;
if (word.length() > m * n) return false;
boolean[][] visited = new boolean[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (word.charAt(0) == board[i][j]) backtrack(board,visited,word,i,j,0);
if (res) break;
}
}
return res;
}
private void backtrack(char[][] board,boolean[][] visited,String word,int i,int j,int index) {
if (!visited[i][j] && board[i][j] == word.charAt(index)){
if (index == word.length()-1) {
res = true;return;
}
visited[i][j] = true;
for (int k = 0; k < 4; k++) {
int x = i + directions[k][0];
int y = j + directions[k][1];
if (isIn(x,y) && !visited[x][y] && board[x][y] == word.charAt(index+1)){
backtrack(board,visited,word,x,y,index+1);
if (res) return; //有滿足的了,趕緊結束遞歸
}
}
visited[i][j] = false; //回溯
}
}
private boolean isIn(int i,int j){
return !(i < 0 || j < 0 || i >= m || j >= n);
}
}
257. 二叉樹的所有路徑
簡單
給定一個二叉樹,返回所有從根節點到葉子節點的路徑。
說明: 葉子節點是指沒有子節點的節點。
示例:
輸入:
1
/ \
2 3
\
5
輸出: ["1->2->5", "1->3"]
解釋: 所有根節點到葉子節點的路徑為: 1->2->5, 1->3
class Solution {
List<String> result = new ArrayList<>();
public List<String> binaryTreePaths(TreeNode root) {
if (root == null) return result;
StringBuilder sb = new StringBuilder();
helper(root,sb);
return result;
}
private void helper(TreeNode root, StringBuilder sb) {
if (root == null) return;
String str = "" + root.val + "->";
sb.append(str);
if (root.left == null && root.right == null)
result.add(sb.toString().substring(0,sb.length()-2));
else {
helper(root.left,sb);
helper(root.right,sb);
}
sb.delete(sb.length()-str.length(),sb.length());
}
}
46. 全排列
中等
給定一個 沒有重復 數字的序列,返回其所有可能的全排列。
示例:
輸入: [1,2,3]
輸出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
class Solution {
List<List<Integer>> result = new LinkedList<>();
List<Integer> restore = new LinkedList<>();
public List<List<Integer>> permute(int[] nums) {
helper(nums);
return result;
}
private void helper(int[] nums) {
if (nums.length == restore.size()) {
result.add(new LinkedList<>(restore)); //必須要初始化※
//result.add(restore); //錯的
return;
}
for (int num : nums) {
if (restore.contains(num)) continue;
restore.add(num);
helper(nums);
restore.remove(restore.size() - 1);
}
}
}
47. 全排列 II
中等
給定一個可包含重復數字的序列,返回所有不重復的全排列。
示例:
輸入: [1,1,2]
輸出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
class Solution {
Set<List<Integer>> result = new HashSet<>();
List<Integer> restore = new LinkedList<>();
Set<Integer> set = new HashSet<>();
public List<List<Integer>> permuteUnique(int[] nums) {
helper(nums);
return new ArrayList<>(result);
}
private void helper(int[] nums) {
if (nums.length == restore.size()) {
result.add(new LinkedList<>(restore));
return;
}
for (int i = 0; i < nums.length; i++) {
int num = nums[i];
if (set.contains(i)) continue;
restore.add(num);
set.add(i);
helper(nums);
restore.remove(restore.size() - 1);
set.remove(i);
}
}
}
77. 組合
中等
給定兩個整數 n 和 k,返回 1 ... n 中所有可能的 k 個數的組合。
示例:
輸入: n = 4, k = 2
輸出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> restore = new ArrayList<>();
Set<Integer> set = new HashSet<>();
public List<List<Integer>> combine(int n, int k) {
helper(n,k,0);
return result;
}
private void helper(int n, int k, int count) {
if (count == k){
result.add(new ArrayList<>(restore));
return;
}
for (int i = 1; i <= n; i++) {
if (set.contains(i) || count > 0 && restore.get(count-1) > i) continue;
restore.add(i);
set.add(i);
helper(n,k,count+1);
restore.remove(count);
set.remove(i);
}
}
}
39. 組合總和
中等
給定一個無重復元素的數組 candidates
和一個目標數 target
,找出 candidates
中所有可以使數字和為 target
的組合。
candidates
中的數字可以無限制重復被選取。
說明:
- 所有數字(包括
target
)都是正整數。 - 解集不能包含重復的組合。
示例 1:
輸入:candidates = [2,3,6,7], target = 7,
所求解集為:
[
[7],
[2,2,3]
]
示例 2:
輸入:candidates = [2,3,5], target = 8,
所求解集為:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate
中的每個元素都是獨一無二的。1 <= target <= 500
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> restore = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
helper(candidates,target);
return result;
}
private void helper(int[] candidates, int target) {
if (target == 0) {
result.add(new ArrayList<>(restore));
return;
}
for (int i : candidates) {
if (target-i < 0) break;
if (!restore.isEmpty() && i < restore.get(restore.size()-1)) continue;
restore.add(i);
helper(candidates, target - i);
restore.remove(restore.size()-1);
}
}
}
40. 組合總和 II
中等
給定一個數組 candidates
和一個目標數 target
,找出 candidates
中所有可以使數字和為 target
的組合。
candidates
中的每個數字在每個組合中只能使用一次。
說明:
- 所有數字(包括目標數)都是正整數。
- 解集不能包含重復的組合。
示例 1:
輸入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集為:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:
輸入: candidates = [2,5,2,1,2], target = 5,
所求解集為:
[
[1,2,2],
[5]
]
class Solution {
Set<List<Integer>> result = new HashSet<>();
List<Integer> restore = new ArrayList<>();
Set<Integer> set = new HashSet<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
helper(candidates,target);
return new ArrayList<>(result);
}
private void helper(int[] candidates, int target) {
if (target == 0) {
result.add(new ArrayList<>(restore));
return;
}
for (int i = 0; i < candidates.length; i++) {
int num = candidates[i];
if (target - num < 0) break;
if (set.contains(i) || !restore.isEmpty() && num < restore.get(restore.size() - 1))continue;
restore.add(num);
set.add(i);
helper(candidates, target - num);
restore.remove(restore.size() - 1);
set.remove(i);
}
}
}
216. 組合總和 III
中等
找出所有相加之和為 n 的 k* 個數的組合。*組合中只允許含有 1 - 9 的正整數,並且每種組合中不存在重復的數字。
說明:
- 所有數字都是正整數。
- 解集不能包含重復的組合。
示例 1:
輸入: k = 3, n = 7
輸出: [[1,2,4]]
示例 2:
輸入: k = 3, n = 9
輸出: [[1,2,6], [1,3,5], [2,3,4]]
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> restore = new ArrayList<>();
public List<List<Integer>> combinationSum3(int k, int n) {
helper(n,k,0);
return result;
}
private void helper(int n, int k, int count) {
if (count == k && n == 0) result.add(new ArrayList<>(restore));
if (count >= k) return;
int i = restore.isEmpty() ? 1 : restore.get(restore.size()-1)+1;
for (; i <= 9; i++) {
restore.add(i);
helper(n-i,k,count+1);
restore.remove(restore.size()-1);
}
}
}
78. 子集
中等
給定一組不含重復元素的整數數組 nums,返回該數組所有可能的子集(冪集)。
說明:解集不能包含重復的子集。
示例:
輸入: nums = [1,2,3]
輸出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> restore = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
Arrays.sort(nums);
helper(nums);
return result;
}
private void helper(int[] nums) {
result.add(new ArrayList<>(restore));
if (restore.size() == nums.length) return;
for (int i = 0; i < nums.length; i++) {
int num = nums[i];
if (!restore.isEmpty() && restore.get(restore.size()-1) >= num) continue;
restore.add(num);
helper(nums);
restore.remove(restore.size()-1);
}
}
}
90. 子集 II
中等
給定一個可能包含重復元素的整數數組 nums,返回該數組所有可能的子集(冪集)。
說明:解集不能包含重復的子集。
示例:
輸入: [1,2,2]
輸出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> restore = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
help(nums, 0);
return result;
}
private void help(int[] nums, int index) {
if (!result.contains(restore)) result.add(new ArrayList<>(restore));
if (index == nums.length) return;
for (int i = index; i < nums.length; i++) {
restore.add(nums[i]);
help(nums, i+1);
restore.remove(restore.size()-1);
}
}
}
131. 分割回文串
中等
給定一個字符串 s,將 s 分割成一些子串,使每個子串都是回文串。
返回 s 所有可能的分割方案。
示例:
輸入: "aab"
輸出:
[
["aa","b"],
["a","a","b"]
]
class Solution {
List<List<String>> result = new ArrayList<>();
List<String> restore = new ArrayList<>();
public List<List<String>> partition(String s) {
helper(s,0,s.length());
return result;
}
private void helper(String s,int index, int end) {
if (index == end) {
result.add(new ArrayList<>(restore));
return;
}
for (int i = index+1; i <= end; i++) {
String substring = s.substring(index, i);
if (isPalindrome(substring)){
restore.add(substring);
helper(s,i,end);
restore.remove(restore.size()-1);
}
}
}
private boolean isPalindrome(String str){
int len = str.length();
for (int i = 0; i < len/2; i++) {
if (str.charAt(i) != str.charAt(len-i-1))
return false;
}
return true;
}
}
37. 解數獨
困難
編寫一個程序,通過已填充的空格來解決數獨問題。
一個數獨的解法需遵循如下規則:
- 數字
1-9
在每一行只能出現一次。 - 數字
1-9
在每一列只能出現一次。 - 數字
1-9
在每一個以粗實線分隔的3x3
宮內只能出現一次。
空白格用 '.'
表示。
一個數獨。
答案被標成紅色。
Note:
- 給定的數獨序列只包含數字
1-9
和字符'.'
。 - 你可以假設給定的數獨只有唯一解。
- 給定數獨永遠是
9x9
形式的。
class Solution {
Set<Character>[] sets1 = new HashSet[9]; //橫向
Set<Character>[] sets2 = new HashSet[9]; //豎向
Set<Character>[] sets3 = new HashSet[9]; //塊
public Solution() {
for (int i = 0; i < 9; i++) {
sets1[i] = new HashSet<>();
sets2[i] = new HashSet<>();
sets3[i] = new HashSet<>();
}
}
public void solveSudoku(char[][] board) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] == '.') continue;
sets1[i].add(board[i][j]);
sets2[j].add(board[i][j]);
sets3[i/3*3 + j/3].add(board[i][j]);
}
}
helper(board,0,0);
}
private boolean helper(char[][] board,int i,int j) {
if (j == 9){
i++;
j = 0;
if (i == 9) return true; //全部填完了
}
if (board[i][j] == '.') {
int block = i/3*3 + j/3;
for (int num = 1; num <= 9; num++) {
char ch = (char)('0'+num);
if (sets1[i].contains(ch) || sets2[j].contains(ch) || sets3[block].contains(ch)) continue;
board[i][j] = ch;
sets1[i].add(ch);
sets2[j].add(ch);
sets3[block].add(ch);
if (helper(board,i,j+1)) return true; //填對了,后邊就不用變回'.'了
board[i][j] = '.';
sets1[i].remove(ch);
sets2[j].remove(ch);
sets3[block].remove(ch);
}
}else {
return helper(board, i, j+1);
}
return false; //沒找到能匹配的數
}
}
51. N皇后
困難
n 皇后問題研究的是如何將 n 個皇后放置在 n×n 的棋盤上,並且使皇后彼此之間不能相互攻擊。
上圖為 8 皇后問題的一種解法。
給定一個整數 n,返回所有不同的 n 皇后問題的解決方案。
每一種解法包含一個明確的 n 皇后問題的棋子放置方案,該方案中 'Q'
和 '.'
分別代表了皇后和空位。
示例:
輸入: 4
輸出: [
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
解釋: 4 皇后問題存在兩個不同的解法。
class Solution {
List<List<String>> result = new ArrayList<>();
char[][] board;
public List<List<String>> solveNQueens(int n) {
board = new char[n][n];
init(board);
helper(0,n);
return result;
}
private void helper(int rowIndex, int colIndex) {
if (rowIndex == board.length) {
result.add(new ArrayList<>(generate(board)));
return;
}
for (int i = 0; i < colIndex; i++) {
if (canfill(rowIndex,i)){
board[rowIndex][i] = 'Q';
helper(rowIndex+1, colIndex);
board[rowIndex][i] = '.';
}
}
}
private boolean canfill(int x, int y) {
for (int i = x-1,j = y;i >=0;i--)
if (board[i][j] == 'Q') return false;
for (int i = x-1,j = y-1;i >=0 && j >= 0;i--,j--)
if (board[i][j] == 'Q') return false;
for (int i = x-1,j = y+1;i >=0 && j <= board.length-1;i--,j++)
if (board[i][j] == 'Q') return false;
return true;
}
private List<String> generate(char[][] board) {
List<String> rows = new ArrayList<>();
for (int i = 0; i < board.length; i++)
rows.add(new String(board[i]));
return rows;
}
private void init(char[][] board) {
for (int i = 0; i < board.length; i++)
for (int j = 0; j < board.length; j++)
board[i][j] = '.';
}
}
頻率排序
廣度:913,815,407,1036,864,199,505,847,200,994,773,863,743
深度:546,679,711,928,394,329,199,834,505,124,488,200,839,364,863,695,753,743,694
回溯:411,93,46,10,37,51,44,22