文章目录
- BFS
- DFS
- [695 岛屿的最大面积](https://leetcode-cn.com/problems/max-area-of-island/)
- [200 岛屿的个数](https://leetcode-cn.com/problems/number-of-islands/)
- [547 朋友圈](https://leetcode-cn.com/problems/friend-circles/)
- [130 被围绕的区域](https://leetcode-cn.com/problems/surrounded-regions/)
- [417 太平洋大西洋水流问题](https://leetcode-cn.com/problems/pacific-atlantic-water-flow/)
- 回溯算法
- [数字键盘组合](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/)
- [93 复原IP地址](https://leetcode-cn.com/problems/restore-ip-addresses/)
- [79 单词搜索](https://leetcode-cn.com/problems/word-search/)
- [257 二叉树的所有路径](https://leetcode-cn.com/problems/binary-tree-paths/)
- [46 全排列](https://leetcode-cn.com/problems/permutations/)
- [47 全排列 II](https://leetcode-cn.com/problems/permutations-ii/)
- [77 组合](https://leetcode-cn.com/problems/combinations/)
- [39 组合求和](https://leetcode-cn.com/problems/combination-sum/)
- [40 组合求和II](https://leetcode-cn.com/problems/combination-sum-ii/)
- [78 子集](https://leetcode-cn.com/problems/subsets/)
- [90 子集 II](https://leetcode-cn.com/problems/subsets-ii/)
- [131 分割回文串](https://leetcode-cn.com/problems/palindrome-partitioning/)
- [37 解数独](https://leetcode-cn.com/problems/sudoku-solver/)
BFS
BFS 只能求解无权图的最短路径
在程序实现 BFS 时需要考虑以下问题:
- 队列:用来存储每一轮遍历得到的节点;
- 标记:对于遍历过的节点,应该将它标记,防止重复遍历。
计算在网格中从原点到特定点的最短路径长度
[[1,1,0,1],
[1,0,1,0],
[1,1,1,1],
[1,0,1,1]]
1 表示可以经过某个位置,求解从 (0, 0) 位置到 (tr, tc) 位置的最短路径长度。
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;
}
279 完全平方数
解法1 BFS
public int numSquares(int n) {
List<Integer> squares=generateSquares(n);
Queue<Integer> queue=new LinkedList<>();
boolean hasVisited[]=new boolean[n+1];
queue.add(n);
hasVisited[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;
}else if(next==0){
return level;
}else if(!hasVisited[next]){
hasVisited[next]=true;
queue.add(next);
}
}
}
}
return n;
}
//生成平方数小于n的数列,{1,4,9...}
public List<Integer> generateSquares(int n){
int square=1;
List<Integer> result=new ArrayList<>();
int diff=3;
while(square<=n){
result.add(square);
square+=diff;
diff+=2;
}
return result;
}
解法2 动态规划
f
(
n
)
=
1
+
m
i
n
(
f
(
n
−
1
2
)
,
f
(
n
−
2
2
)
,
.
.
.
,
f
(
n
−
k
2
)
)
,
其
中
k
为
k
2
<
=
n
f(n)=1+min(f(n-1^2),f(n-2^2),...,f(n-k^2)), 其中k为k^2<=n
f(n)=1+min(f(n−12),f(n−22),...,f(n−k2)),其中k为k2<=n
public int numSquares(int n) {
int dp[]=new int[n+1];
for(int i=1;i<=n;i++){
int min=Integer.MAX_VALUE;
for(int j=1;j*j<=i;j++){
min=Math.min(min,dp[i-j*j]+1);
}
dp[i]=min;
}
return dp[n];
}
解法3 四平方定理
Lagrange 四平方定理: 任何一个正整数都可以表示成不超过四个整数的平方之和。(万能的拉格朗日Orz)
还有一个重要的推论
先判断这个数是否满足
4
k
(
8
b
+
7
)
4^k(8b+7)
4k(8b+7),若满足,则为4,然后暴力破解1和2,若都不是则返回3
public int numSquares(int n) {
if(n<=0)
return -1;
while(n%4==0)
n/=4;
if(n%8==7)
return 4;
for(int i=0;i*i<=n;i++){
int j=(int)Math.sqrt(n-i*i);
if(i*i+j*j==n){
if(i!=0 && j!=0){
return 2;
}else{
return 1;
}
}
}
return 3;
}
127 单词接龙
采用两端搜索:
用HashSet存储StartWord和endWord以及字典中的单词。每次从startWord向着endWord转换,但是每一步出现的中间结果存储到一个临时Set集合,假设为tempSet中,若该临时结果数目小于endWord的数目,则
startWord=tempSet;
否则,
startWord=endWord;
endWord=tempSet;
因此,我们每次都从中间结果少的那一端出发,这样就能剪枝掉很多不必要的搜索过程。
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
if (wordList == null || wordList.size() == 0) {
return 0;
}
HashSet<String> start = new HashSet<>();
HashSet<String> end = new HashSet<>();
HashSet<String> dic = new HashSet<>(wordList);
start.add(beginWord);
end.add(endWord);
if (!dic.contains(endWord))
return 0;
int step = 1;
while (!start.isEmpty()) {
step++;
dic.removeAll(start);
HashSet<String> tempSet = new HashSet<>();
for (String str : start) {
char[] arr = str.toCharArray();
for (int i = 0; i < arr.length; i++) {
char tempIndexI = arr[i];
for (char c = 'a'; c <= 'z'; c++) {
if (tempIndexI == c)
continue;
arr[i] = c;
String temp_str = new String(arr);
if (dic.contains(temp_str)) {
if (end.contains(temp_str)) {
return step;
} else {
tempSet.add(temp_str);
}
}
}
arr[i] = tempIndexI;
}
}
if (tempSet.size() < end.size()) {
start = tempSet;
} else {
start = end;
end = tempSet;
}
}
return 0;
}
}
DFS
常用来解决可达性问题,需要回溯的算法在下一章专门介绍
在程序实现 DFS 时需要考虑以下问题:
- 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。
- 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。
695 岛屿的最大面积
每次访问到某个点时,设置grid[x][y]=0;
,此题稍加改动也可以用来计算联通岛屿的个数,如下面另一个道求岛屿的个数
不过该方法会改变里面的grid,如果不想改变,可以拷贝一份grid数据。
class Solution {
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++){
if(grid[i][j]!=0){
maxArea=Math.max(maxArea,dfs(grid,i,j));
}
}
}
return maxArea;
}
public int dfs(int[][] grid,int x,int y){
if(x<0 || x>=m || y<0 || y>=n || grid[x][y]==0)
return 0;
grid[x][y]=0;
int area=1;
for(int d[]:direction){
area += dfs(grid, x + d[0], y + d[1]);
}
return area;
}
}
200 岛屿的个数
原理和上题一样,是上题的变形
class Solution {
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 count=0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]!='0'){
dfs(grid,i,j);
count++;
}
}
}
return count;
}
public void dfs(char[][] grid,int x,int y){
if(x<0 || x>=m || y<0 || y>=n || grid[x][y]=='0')
return ;
grid[x][y]='0';
for(int d[]:direction){
dfs(grid, x + d[0], y + d[1]);
}
}
}
547 朋友圈
思想和上面两题差不多。只是遍历时只需要按人数遍历。
class Solution {
public int findCircleNum(int[][] M) {
if(M==null || M.length==0)
return 0;
int n=M.length;
boolean hasVisited[]=new boolean[n];
int circurtNum=0;
for(int i=0;i<n;i++){
if(!hasVisited[i]){
circurtNum++;
dfs(M,i,hasVisited);
}
}
return circurtNum;
}
public void dfs(int[][] M,int i,boolean[] hasVisited){
hasVisited[i]=true;
for(int j=0;j<M.length;j++){
if(M[i][j]==1 && !hasVisited[j]){
dfs(M,j,hasVisited);
}
}
}
}
130 被围绕的区域
思路:先填充最外侧,剩下的就是里侧了。所有能被外侧‘O’遍历到的都是不被围绕的,否则是被围绕的。在遍历时用中间状态‘T’来表示能被外侧‘O’遍历到的‘O’.
class Solution {
private int[][] direction={{0,1},{0,-1},{1,0},{-1,0}};
public void solve(char[][] board) {
if(board==null || board.length==0)
return ;
int m=board.length;
int n=board[0].length;
for(int i=0;i<m;i++){
dfs(board,i,0);
dfs(board,i,n-1);
}
for(int j=1;j<n-1;j++){
dfs(board,0,j);
dfs(board,m-1,j);
}
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';
}
}
}
}
public void dfs(char[][] board,int i,int j){
if(i<0 || i>=board.length || j<0 || j>=board[0].length || board[i][j]!='O'){
return ;
}
board[i][j]='T';
for(int []dir:direction){
dfs(board,i+dir[0],j+dir[1]);
}
}
}
417 太平洋大西洋水流问题
思路:从矩阵边界开始深度遍历,用canReachP记录是否能到达太平洋,用canReachA记录能否到达大西洋。最后找出两者都能到达的坐标。
class Solution {
private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
public List<int[]> pacificAtlantic(int[][] matrix) {
List<int[]> result=new ArrayList<>();
if(matrix==null || matrix.length==0)
return result;
int m=matrix.length;
int n=matrix[0].length;
boolean [][]canReachP=new boolean[m][n];
boolean [][]canReachA=new boolean[m][n];
for(int i=0;i<m;i++){
dfs(matrix,i,0,canReachP);
dfs(matrix,i,n-1,canReachA);
}
for(int j=0;j<n;j++){ //注意和上面记到的区别,此处必须是(0<=j<n),因为右上角和左下角既能达到太平洋又能达到大西洋
dfs(matrix,0,j,canReachP);
dfs(matrix,m-1,j,canReachA);
}
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(canReachP[i][j]&&canReachA[i][j]){
result.add(new int[]{i,j});
}
}
}
return result;
}
public void dfs(int[][] matrix,int i,int j,boolean [][]canReach){
if(canReach[i][j]){
return ;
}
canReach[i][j]=true;
for(int dir[]:direction){
int nextI=i+dir[0];
int nextJ=j+dir[1];
if(nextI>=0 && nextI<matrix.length && nextJ>=0 && nextJ<matrix[0].length && matrix[i][j]<=matrix[nextI][nextJ]){
dfs(matrix,i+dir[0],j+dir[1],canReach);
}
}
}
}
回溯算法
Backtracking(回溯)属于 DFS。
- 普通 DFS 主要用在 可达性问题 ,这种问题只需要执行到特点的位置然后返回即可。
- 而 Backtracking 主要用于求解 排列组合 问题,例如有 { ‘a’,‘b’,‘c’ } 三个字符,求解所有由这三个字符排列得到的字符串,这种问题在执行到特定的位置返回之后还会继续执行求解过程。
因为 Backtracking 不是立即返回,而要继续求解,因此在程序实现时,需要注意对元素的标记问题:
- 在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;
- 但是在递归返回时,需要将元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。
回溯算法注意如何进行剪枝操作进行优化
数字键盘组合
class Solution {
private static String[] KEYS={"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public List<String> letterCombinations(String digits) {
List<String> result=new ArrayList<>();
if(digits==null || digits.length()==0)
return result;
doCombination(digits,new StringBuilder(),result);
return result;
}
public void doCombination(String digits,StringBuilder curLetters,List<String> result){
int lettersLen=curLetters.length();
if(lettersLen==digits.length()){
result.add(curLetters.toString());
return ;
}
int curDigit=digits.charAt(lettersLen)-'0';
char []arr = (KEYS[curDigit-2]).toCharArray();
for(char c:arr){
curLetters.append(c);
doCombination(digits,curLetters,result);
curLetters.deleteCharAt(curLetters.length()-1);
}
}
}
另外一种解法:
class Solution {
public List<String> letterCombinations(String digits) {
LinkedList<String> result=new LinkedList<>();
if(digits==null || digits.length()==0)
return result;
String [] mapping={"0","1","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
result.add("");
while(result.peek().length()!=digits.length()){
String curStr=result.remove();
String map=mapping[digits.charAt(curStr.length())-'0'];
for(char c:map.toCharArray()){
result.addLast(curStr+c);
}
}
return result;
}
}
93 复原IP地址
剪枝操作:字符串s剩余的长度要在 剩余的ip地址范围内
class Solution {
public List<String> restoreIpAddresses(String s) {
List<String> result =new ArrayList<>();
if(s==null || s.length()==0){
return result;
}
getCombinations(s,result,new StringBuilder(),0);
return result;
}
public void getCombinations(String s,List<String> result,StringBuilder curAdress,int k){
if(k==4 || s.length()==0){
if(k==4 && s.length()==0){
result.add(curAdress.toString());
}
return ;
}
//进行剪枝操作,s 剩余的长度要在 剩余的ip地址范围内
if(s.length()<4-k || s.length()>(4-k)*3)
return ;
for(int i=0;i<s.length() && i<=2;i++){
if(i!=0 && s.charAt(0)=='0'){
break;
}
String tmp=s.substring(0,i+1);
if(Integer.valueOf(tmp)<=255){
if(curAdress.length()!=0){
tmp="."+tmp;
}
curAdress.append(tmp);
getCombinations(s.substring(i+1),result,curAdress,k+1);
curAdress.delete(curAdress.length()-tmp.length(),curAdress.length());
}
}
}
}
另解:
class Solution {
public List<String> restoreIpAddresses(String s) {
List<String> result = new ArrayList<>();
if (s == null || s.length() == 0) {
return result;
}
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
for (int k = 1; k <= 3; k++) {
for (int l = 1; l <= 3; l++) {
if (i + j + k +l == s.length()) {
int a = Integer.parseInt(s.substring(0, i));
int b = Integer.parseInt(s.substring(i, i + j));
int c = Integer.parseInt(s.substring(i + j, i + j + k));
int d = Integer.parseInt(s.substring(i + j+k, i + j + k+l));
if (a <= 255 && b <= 255 && c <= 255 && d<=255) {
String tmp=a + "." + b + "." + c + "." + d;
if(tmp.length()==s.length()+3){
result.add(a + "." + b + "." + c + "." + d);
}
}
}
}
}
}
}
return result;
}
}
79 单词搜索
board[i][j]^=256
; 因为board的所有元素二进制最高位都为0,故利用设置最高位为1来改变board,而不用额外空间设置是否访问。
class Solution {
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;
}
char w[] = word.toCharArray();
for(int i=0;i<board.length;i++){
for(int j=0;j<board[0].length;j++){
if(exist(board,w,i,j,0))
return true;
}
}
return false;
}
public boolean exist(char[][] board,char[] word,int i,int j,int len){
if(len==word.length)
return true;
if(i<0 || j<0 || i>=board.length || j>=board[0].length || board[i][j]!=word[len])
return false;
board[i][j]^=256;
boolean exist=exist(board,word,i+1,j,len+1) || exist(board,word,i-1,j,len+1) || exist(board,word,i,j+1,len+1) || exist(board,word,i,j-1,len+1);
board[i][j]^=256;
return exist;
}
}
257 二叉树的所有路径
注意这里用的是常量池的String,而不是使用new String()
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> result=new ArrayList<>();
if(root==null)
return result;
getPath(root,"",result);
return result;
}
public void getPath(TreeNode node,String path,List<String> result){
if(node==null){
return;
}
path=path+node.val;
if(node.left==null&&node.right==null){
result.add(path);
}else{
getPath(node.left,path+"->",result);
getPath(node.right,path+"->",result);
}
}
}
46 全排列
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result=new ArrayList();
if(nums==null || nums.length==0)
return result;
boolean hasVisited[]=new boolean[nums.length];
backTrace(nums,result,new ArrayList<>(),hasVisited);
return result;
}
public void backTrace(int []nums,List<List<Integer>> result,List<Integer> tempList,boolean[] hasVisited){
if(tempList.size()==nums.length){
result.add(new ArrayList<Integer>(tempList));
return ;
}
for(int i=0;i<nums.length;i++){
if(!hasVisited[i]){
hasVisited[i]=true;
tempList.add(nums[i]);
backTrace(nums,result,tempList,hasVisited);
tempList.remove(tempList.size()-1);
hasVisited[i]=false;
}
}
}
47 全排列 II
与上一题相比有两步不一样:
1 Arrays.sort(nums);//需要先排序,为后面判断创建条件
2 if(hasVisited[i] || i>0 && nums[i]==nums[i-1] && !hasVisited[i-1])continue;
即当当前元素已经被访问过或者当前元素不是第一个元素且与上一个元素值相等且上一个元素没有被使用过,就跳过此次运行,进入下一步循环。
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> result=new ArrayList();
if(nums==null || nums.length==0)
return result;
Arrays.sort(nums);//1
boolean hasVisited[]=new boolean[nums.length];
backTrace(nums,result,new ArrayList<>(),hasVisited);
return result;
}
public void backTrace(int []nums,List<List<Integer>> result,List<Integer> tempList,boolean[] hasVisited){
if(tempList.size()==nums.length){
result.add(new ArrayList<Integer>(tempList));
return ;
}
for(int i=0;i<nums.length;i++){
if(hasVisited[i] || i>0 && nums[i]==nums[i-1] && !hasVisited[i-1]){
continue;
}//2
hasVisited[i]=true;
tempList.add(nums[i]);
backTrace(nums,result,tempList,hasVisited);
tempList.remove(tempList.size()-1);
hasVisited[i]=false;
}
}
}
77 组合
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> result = new ArrayList();
if (n<k || n<1 || k<1)
return result;
backTrace(n, k, result, new ArrayList<>(), 1);
return result;
}
public void backTrace(int n, int remain, List<List<Integer>> result, List<Integer> tempList, int start) {
if (remain==0) {
result.add(new ArrayList<Integer>(tempList));
return;
}
for (int i = start; i <= n-remain+1; i++) {//进行剪枝操作
tempList.add(i);
backTrace(n, remain-1, result,tempList, i+1);
tempList.remove(tempList.size() - 1);
}
}
}
39 组合求和
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> result = new ArrayList();
if(candidates==null || candidates.length==0)
return result;
Arrays.sort(candidates);//先进行排序才能应用后面的剪枝操作
backTrace(candidates, target, result,new ArrayList<>(),0);
return result;
}
public void backTrace(int []candidates, int target, List<List<Integer>> result, List<Integer> tempList, int start){
if(target<0){
return;
}if(target==0){
result.add(new ArrayList<Integer>(tempList));
}else{
for(int i=start;i<candidates.length;i++){
if(candidates[i]>target){//进行剪枝操作,因为前面已经将数据排序
return ;
}else{
tempList.add(candidates[i]);
backTrace(candidates,target-candidates[i],result,tempList,i);//注意这里是i因为数据可以重复
tempList.remove(tempList.size() - 1);
}
}
}
}
}
40 组合求和II
注意一下这里的去重判断if(i>start && candidates[i]==candidates[i-1])
,即已排序的重复数字中,如果前面的重复数字没有用过,那即后面重复的数字将跳过,已避免重复。
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> result = new ArrayList();
if(candidates==null || candidates.length==0)
return result;
Arrays.sort(candidates);//同样排序,以便剪枝操作
backTrace(candidates, target, result,new ArrayList<>(),0);
return result;
}
public void backTrace(int []candidates, int target, List<List<Integer>> result, List<Integer> tempList, int start){
if(target<0){
return;
}if(target==0){
result.add(new ArrayList<Integer>(tempList));
}else{
for(int i=start;i<candidates.length;i++){
if(candidates[i]>target){//剪枝
return ;
}else if(i>start && candidates[i]==candidates[i-1]){//去重
continue;
}else{
tempList.add(candidates[i]);
backTrace(candidates,target-candidates[i],result,tempList,i+1);//这里是i+1,因为不能重复
tempList.remove(tempList.size() - 1);
}
}
}
}
}
78 子集
和77组合套路差不多,只是多了for循环进行不同数字个数的排列
解法一:
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result = new ArrayList();
if(nums==null || nums.length==0)
return result;
for(int i=0;i<=nums.length;i++){
backTrace(nums, i, result, new ArrayList<>(), 0);
}
return result;
}
public void backTrace(int []nums, int remain, List<List<Integer>> result, List<Integer> tempList, int start){
if(remain==0){
result.add(new ArrayList<Integer>(tempList));
return;
}
for (int i = start; i <= nums.length-remain; i++) {
tempList.add(nums[i]);
backTrace(nums, remain-1, result,tempList, i+1);
tempList.remove(tempList.size() - 1);
}
}
}
解法二:
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result = new ArrayList();
if(nums==null || nums.length==0)
return result;
backTrace(nums, result, new ArrayList<>(), 0);
return result;
}
public void backTrace(int []nums, List<List<Integer>> result, List<Integer> tempList, int start){
result.add(new ArrayList<Integer>(tempList));
for(int i=start;i<nums.length;i++){
tempList.add(nums[i]);
backTrace(nums, result, tempList, i+1);
tempList.remove(tempList.size() - 1);
}
}
}
90 子集 II
与上一题套路一样,只是多了先排序后去重判断,与40题的去重判断一模一样。
if(i>start && nums[i]==nums[i-1]){
continue;
}
解法一:
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> result = new ArrayList();
if(nums==null || nums.length==0)
return result;
Arrays.sort(nums);
for(int i=0;i<=nums.length;i++){
backTrace(nums, i, result, new ArrayList<>(), 0);
}
return result;
}
public void backTrace(int []nums, int remain, List<List<Integer>> result, List<Integer> tempList, int start){
if(remain==0){
result.add(new ArrayList<Integer>(tempList));
return;
}
for (int i = start; i <= nums.length-remain; i++) {
if(i>start && nums[i]==nums[i-1]){
continue;
}
tempList.add(nums[i]);
backTrace(nums, remain-1, result,tempList, i+1);
tempList.remove(tempList.size() - 1);
}
}
}
解法二也一样:
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> result = new ArrayList();
if(nums==null || nums.length==0)
return result;
Arrays.sort(nums);
backTrace(nums, result, new ArrayList<>(), 0);
return result;
}
public void backTrace(int []nums, List<List<Integer>> result, List<Integer> tempList, int start){
result.add(new ArrayList<Integer>(tempList));
for(int i=start;i<nums.length;i++){
if(i>start && nums[i]==nums[i-1]){
continue;
}
tempList.add(nums[i]);
backTrace(nums, result, tempList, i+1);
tempList.remove(tempList.size() - 1);
}
}
}
131 分割回文串
class Solution {
public List<List<String>> partition(String s) {
List<List<String>> result = new ArrayList();
if(s==null || s.length()==0){
return result;
}
doPartition(s, result, new ArrayList<>());
return result;
}
public void doPartition(String s, List<List<String>> result, List<String> tempList){
if(s.length()==0){
result.add(new ArrayList<String>(tempList));
return ;
}
for(int i=0;i<s.length();i++){
if(isPalindrome(s,0,i)){
tempList.add(s.substring(0,i+1));
doPartition(s.substring(i+1),result, tempList);
tempList.remove(tempList.size()-1);
}
}
}
public boolean isPalindrome(String s,int start,int end){
while(start<end){
if(s.charAt(start++)!=s.charAt(end--)){
return false;
}
}
return true;
}
}
37 解数独
其实思想是一样的,只不过重点在于设计三个数组:
private boolean rowUsed[][]=new boolean[9][10];//rowUsed[i][j]代表第i行已有数字j
private boolean columnUsed[][]=new boolean[9][10];//columnUsed[i][j]代表第i列已有数字j
private boolean cubesUsed[][]=new boolean[9][10];//cubes[i][j]代表第i块已有数字j,一共有九块
class Solution {
private boolean rowUsed[][]=new boolean[9][10];
private boolean columnUsed[][]=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<board.length;i++){
for(int j=0;j<board[0].length;j++){
if(board[i][j]!='.'){
int num=board[i][j]-'0';
rowUsed[i][num]=true;
columnUsed[j][num]=true;
cubesUsed[(i/3)*3+j/3][num]=true;
}
}
}
backTrace(0,0);
}
public boolean backTrace(int row,int column){
while(row<9&&board[row][column]!='.'){
row=column==8?row+1:row;
column=column==8?0:column+1;
}
if(row==9){
return true;
}
for(int num=1;num<=9;num++){
if(rowUsed[row][num]||columnUsed[column][num]||cubesUsed[row/3*3+column/3][num]){
continue;
}
rowUsed[row][num]=columnUsed[column][num]=cubesUsed[row/3*3+column/3][num]=true;
board[row][column]=(char)(num+'0');
if(backTrace(row,column)){
return true;
}
rowUsed[row][num]=columnUsed[column][num]=cubesUsed[row/3*3+column/3][num]=false;
board[row][column]='.';
}
return false;
}
}