一、组合:同一个元素不能重复使用
1.1 组合
77:给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
class Solution {LinkedList<List<Integer>> result = new LinkedList<>();LinkedList<Integer> path = new LinkedList<>();public List<List<Integer>> combine(int n, int k) {backtracking(n,k,1);return result;}public void backtracking(int n,int k,int startindex){if(path.size()==k){//不能直接放path,因为传进去是引用,后面path继续修改会影响之前的path结果result.add(new LinkedList<>(path));return ;}//减枝优化:还需要在n个元素中再找k-path.size个元素,所以i的位置最多是n-(k-path.size())+1for(int i =startindex;i<=n-(k-path.size())+1;i++){path.offer(i);backtracking(n,k,i+1);path.pollLast();}}
}
1.2 组合之和
216:找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
与上题相比,多了个sum的限制
class Solution {LinkedList<List<Integer>> result = new LinkedList<>();LinkedList<Integer> path = new LinkedList<>();public List<List<Integer>> combinationSum3(int k, int n) {backtracking(k,n,1);return result;}public void backtracking(int k,int sum,int startindex){if(sum<0||path.size()>k)return ;if(sum==0&&path.size()==k){result.add(new LinkedList(path));return ;}for(int i=startindex;i<10;i++){path.add(i);sum -= i;backtracking(k,sum,i+1);sum += i;path.pollLast();}}
}
二、组合进阶
2.1 组合总和:candidates无重复元素,可以重复拿取同一个元素
39:给你一个无重复元素的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有不同组合 ,并以列表形式返回。你可以按任意顺序返回这些组合。candidates 中的同一个数字可以无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
class Solution {LinkedList<Integer> path = new LinkedList<>();List<List<Integer>> result = new LinkedList<>();public List<List<Integer>> combinationSum(int[] candidates, int target) {backtracking(candidates,target,0);return result;}public void backtracking(int[] candidates,int target,int startindex){if(target<0)return;if(target==0){result.add(new LinkedList(path));return ;}for(int i=startindex;i<candidates.length;i++){path.offer(candidates[i]);//下一层 下标从i开始,代表可以重复选取相同元素backtracking(candidates,target-candidates[i],i);path.pollLast();}}
}
2.2 组合总和:candidates有重复元素,不能重复拿取同一个元素
40:给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。
class Solution {List<List<Integer>> result = new LinkedList<>();LinkedList<Integer> path = new LinkedList<>();public List<List<Integer>> combinationSum2(int[] candidates, int target) {boolean[] used = new boolean[candidates.length];Arrays.sort(candidates);backtracking(candidates,target,0,used);return result;}public void backtracking(int[] candidates,int target,int startindex,boolean[] used){if(target<0)return ;if(target==0){result.add(new LinkedList(path));return ;}for(int i=startindex;i<candidates.length;i++){//说明是同一层之前的元素if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false)continue;//used[i]!=false,说明是上下层关系,used[i] = true;path.offer(candidates[i]);backtracking(candidates,target-candidates[i],i+1,used);path.pollLast();used[i] = false;}}
}
三、排列
3.1 全排列
给定一个不含重复数字的数组 nums ,返回其所有可能的全排列 。你可以按任意顺序返回答案。
class Solution {List<List<Integer>> result = new LinkedList<>();LinkedList<Integer> path = new LinkedList<>();public List<List<Integer>> permute(int[] nums) {boolean[] used = new boolean[nums.length];backtracking(nums,used);return result;}public void backtracking(int[] nums,boolean[] used){if(path.size()==nums.length){result.add(new LinkedList(path));return;}//下标从0-nums.length-1for(int i=0;i<nums.length;i++){//上一层用过了if(used[i]==true)continue;//这一层要用,让下一层不要用used[i]=true;path.offer(nums[i]);backtracking(nums,used);path.pollLast();//清除标记,让同一层的兄弟儿子可以用 used[i]=false; }return ;}
}
3.2 全排列II
47/剑指offer38:给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
class Solution {List<List<Integer>> result = new LinkedList<>();LinkedList<Integer> path = new LinkedList<>();public List<List<Integer>> permuteUnique(int[] nums) {Arrays.sort(nums);boolean[] used = new boolean[nums.length];backtracking(nums,used);return result;}public void backtracking(int[] nums,boolean[] used){if(path.size()==nums.length){result.add(new LinkedList(path));return ;}//下标从0-nums.length-1for(int i=0;i<nums.length;i++){//左边兄弟和自己元素相等if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false)continue;//上一层用过这个元素if(used[i]==true)continue;used[i]=true;path.offer(nums[i]);backtracking(nums,used);path.pollLast();used[i]=false;}return ;}
}
四、分割字符串
4.1 分割字符串
131:给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文串 。返回 s 所有可能的分割方案。 回文串是正着读和反着读都一样的字符串。
class Solution {List<List<String>> result = new LinkedList<>();LinkedList<String> path = new LinkedList<>();public List<List<String>> partition(String s) {backtracking(s,0);return result;}public void backtracking(String s,int startindex){//如果已经截取完,加入到结果if(startindex>=s.length()){result.add(new LinkedList(path));return ;}//从startindex开始截取,找到[startindex,i]是回文,递归往下一层for(int i=startindex;i<s.length();i++){if(isPalindrome(s,startindex,i)){path.offer(s.substring(startindex,i+1));backtracking(s,i+1);path.pollLast();}}}boolean isPalindrome(String s,int start,int end){while(start<end){if(s.charAt(start)!=s.charAt(end))return false;start++;end--;}return true;}
}
4.2 复原IP地址
93:给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你不能重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
class Solution {List<String> result = new LinkedList<>();public List<String> restoreIpAddresses(String s) {backtracking(s,0,"",0);return result;}public void backtracking(String s,int startindex,String path,int count){if(count>4)return ;if(startindex>=s.length()&&count==4){result.add(path.substring(0,path.length()-1));return ;}for(int i=startindex;i<s.length();i++){if(isValid(s,startindex,i)){backtracking(s,i+1,path + s.substring(startindex,i+1)+".",count+1);}}}public boolean isValid(String s,int start,int end){if(end-start>2)return false;if(end-start>0&&s.charAt(start)=='0')return false;if(Integer.parseInt(s.substring(start,end+1))>255)return false;return true;}
}
4.3 单词拆分II
140:给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。
class Solution {List<String> result = new LinkedList<>();List<String> wordDict = new LinkedList();public List<String> wordBreak(String s, List<String> wordDict) {this.wordDict = wordDict;backtracking(s,0,"");return result;}public boolean isValid(String s,int start,int end){String tmp = s.substring(start,end+1);return wordDict.contains(tmp);}public void backtracking(String s,int startindex,String path){if(startindex==s.length()){path = path.substring(0,path.length()-1);result.add(path);}for(int i=startindex;i<s.length();i++){if(isValid(s,startindex,i)){backtracking(s,i+1,path+s.substring(startindex,i+1)+" ");}}}}
五、子集
78:给你一个整数数组 nums ,数组中的元素互不相同 。返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
与组合类似,不同的是没有终止条件,沿途的记录都放入结果,for循环结束就终止
class Solution {List<List<Integer>> result = new LinkedList<>();LinkedList<Integer> path = new LinkedList<>();public List<List<Integer>> subsets(int[] nums) {backtracking(nums,0);return result;}public void backtracking(int[] nums,int startindex){//没有写终止,沿途的记录都放入结果,for循环结束就终止result.add(new LinkedList(path));for(int i=startindex;i<nums.length;i++){path.offer(nums[i]);backtracking(nums,i+1);path.pollLast();}return ;}
}
六、深度遍历
6.1 岛屿数量
200:给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外,你可以假设该网格的四条边均被水包围。
class Solution {public int numIslands(char[][] grid) {int count = 0;int m = grid.length;int n = grid[0].length;for(int i =0;i<m;i++){for(int j=0;j<n;j++){if(grid[i][j]=='1'){//遇到陆地开始深度遍历dfs(grid,i,j);count++;} }}return count;}public void dfs(char[][] grid,int row,int col){if(!inArea(grid,row,col))return ;//以及遍历过的地方,或者是水域直接返回if(grid[row][col]=='0'||grid[row][col]=='2')return ;//深度遍历到的地方标记为2grid[row][col] = '2';dfs(grid,row+1,col);dfs(grid,row-1,col);dfs(grid,row,col-1);dfs(grid,row,col+1);}public boolean inArea(char[][] grid,int row,int col){return row>=0&&row<grid.length&&col>=0&&col<grid[0].length;}
}
6.2 岛屿的最大面积
695:根据上一题求,岛屿的最大面积
class Solution {public int maxAreaOfIsland(int[][] grid) {int m = grid.length;int n = grid[0].length;int max = 0;for(int i=0;i<m;i++){for(int j=0;j<n;j++){if(grid[i][j]==1){max =Math.max(max,dfs(grid,i,j));}}}return max;}public int dfs(int[][]grid,int row,int col){if(row<0||col<0||row>grid.length-1||col>grid[0].length-1)return 0;if(grid[row][col]==0||grid[row][col]==2)return 0;//染色,标记为岛屿grid[row][col]=2;return 1+dfs(grid,row+1,col)+dfs(grid,row-1,col)+dfs(grid,row,col+1)+dfs(grid,row,col-1);}
}
6.3 单词搜索
79:给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
class Solution {public boolean exist(char[][] board, String word) {int[][] visited = new int[board.length][board[0].length];for(int i=0;i<board.length;i++){for(int j=0;j<board[0].length;j++){if(dfs(board,i,j,word,0,visited))return true;}}return false;}public boolean dfs(char[][] board,int row,int col,String word,int k,int[][] visited){if(row<0||row>board.length-1||col<0||col>board[0].length-1||visited[row][col]==1){return false;}if(board[row][col]!=word.charAt(k))return false;if(board[row][col]==word.charAt(k)&&k==word.length()-1){return true;}//标记这个点已经访问过,下一层不能访问visited[row][col] = 1;boolean up = dfs(board,row-1,col,word,k+1,visited);boolean down = dfs(board,row+1,col,word,k+1,visited);boolean left = dfs(board,row,col-1,word,k+1,visited);boolean right = dfs(board,row,col+1,word,k+1,visited);//擦除标记,自己走完让别的路径方法可以走visited[row][col] = 0;return up||down||left||right;}
}