算法 回溯算法 hiriki 2023-01-10 2023-01-10 组合 leetcode题目链接
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序 返回答案。
示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
思路草稿
题解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { List<Integer> path = new ArrayList <>(); List<List<Integer>> result = new ArrayList <>(); public List<List<Integer>> combine (int n, int k) { backTracking(n,k,1 ); return result; } private void backTracking (int n,int k,int start) { if (path.size()==k){ result.add(new ArrayList <>(path)); return ; } for (int i = start;i<=n-(k-path.size())+1 ;i++){ path.add(i); backTracking(n,k,i+1 ); path.remove(path.size()-1 ); } } }
组合总和III leetcode题目链接
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
思路草稿
题解 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 class Solution { List<Integer> path = new ArrayList <>(); List<List<Integer>> res = new ArrayList <>(); public List<List<Integer>> combinationSum3 (int k, int n) { backTracking(k,n,0 ,1 ); return res; } private void backTracking (int k,int n,int sum,int start) { if (path.size()==k){ if (sum==n){ res.add(new ArrayList <>(path)); } return ; } if (sum>n){ return ; } for (int i = start;i<=9 -(k-path.size())+1 ;i++){ path.add(i); sum+=i; backTracking(k,n,sum,i+1 ); path.remove(path.size()-1 ); sum-=i; } } }
电话号码的字母组合 leetcode题目链接
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
思路草稿
题解 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 class Solution { StringBuilder sb = new StringBuilder (); List<String> res = new ArrayList <>(); String[] map = {"" ,"" ,"abc" ,"def" ,"ghi" ,"jkl" ,"mno" ,"pqrs" ,"tuv" ,"wxyz" }; public List<String> letterCombinations (String digits) { if (digits=="" ||digits.length()==0 ){ return res; } backTracking(digits,0 ); return res; } private void backTracking (String digits,int index) { if (index==digits.length()){ res.add(sb.toString()); return ; } String temp = map[digits.charAt(index)-'0' ]; for (int i = 0 ;i<temp.length();i++){ sb.append(temp.charAt(i)); backTracking(digits,index+1 ); sb.deleteCharAt(sb.length()-1 ); } } }
组合总和 leetcode题目链接
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
思路草稿
题解 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 31 32 33 class Solution { List<Integer> path = new ArrayList <>(); List<List<Integer>> res = new ArrayList <>(); public List<List<Integer>> combinationSum (int [] candidates, int target) { Arrays.sort(candidates); backTracking(candidates,0 ,target,0 ); return res; } private void backTracking (int [] candidates,int sum,int target,int start) { if (sum>target){ return ; } if (sum==target){ res.add(new ArrayList <>(path)); return ; } for (int i = start;i<candidates.length;i++){ if (sum+candidates[i]>target){ break ; } path.add(candidates[i]); sum+=candidates[i]; backTracking(candidates,sum,target,i); path.remove(path.size()-1 ); sum-=candidates[i]; } } }
组合总和II leetcode题目链接
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明: 所有数字(包括目标数)都是正整数。解集不能包含重复的组合。
思路草稿
题解 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 31 32 33 34 35 36 37 38 39 40 41 42 43 class Solution { List<Integer> path = new ArrayList <>(); List<List<Integer>> res = new ArrayList <>(); boolean [] used; public List<List<Integer>> combinationSum2 (int [] candidates, int target) { used = new boolean [candidates.length]; Arrays.fill(used,false ); Arrays.sort(candidates); backTracking(candidates,target,0 ,0 ); return res; } private void backTracking (int [] candidates,int target,int sum,int start) { if (sum>target){ return ; } if (sum==target){ res.add(new ArrayList <>(path)); return ; } for (int i = start;i<candidates.length;i++){ if (sum+candidates[i]>target){ break ; } if (i>0 &&candidates[i]==candidates[i-1 ]&&!used[i-1 ]){ continue ; } path.add(candidates[i]); sum+=candidates[i]; used[i]=true ; backTracking(candidates,target,sum,i+1 ); path.remove(path.size()-1 ); sum-=candidates[i]; used[i]=false ; } } }
分割回文串 leetcode题目链接
给定一个字符串 s
,请将 s
分割成一些子串,使每个子串都是 回文串 。返回 s
所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
思路草稿
题解 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 31 32 33 34 35 36 37 class Solution { List<String> path = new ArrayList <>(); List<List<String>> res = new ArrayList <>(); public List<List<String>> partition (String s) { backTracking(s,0 ); return res; } private void backTracking (String s,int start) { if (start==s.length()){ res.add(new ArrayList <>(path)); return ; } for (int i = start;i<s.length();i++){ if (isPalindrome(s,start,i)){ path.add(s.substring(start,i+1 )); } else { continue ; } backTracking(s,i+1 ); path.remove(path.size()-1 ); } } private boolean isPalindrome (String s,int left,int right) { for (;left<right;left++,right--){ if (s.charAt(left)!=s.charAt(right)){ return false ; } } return true ; } }
复原IP地址 leetcode题目链接
有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。
例如:”0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、”192.168.1.312” 和 “192.168@1.1” 是 无效 IP 地址。 给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 ‘.’ 来形成。 不能 重新排序或删除 s 中的任何数字。可以按 任何 顺序返回答案。
思路草稿
题解 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class Solution { List<String> res = new ArrayList <>(); StringBuilder sb = new StringBuilder (); public List<String> restoreIpAddresses (String s) { backTracking(s,0 ,0 ); return res; } private void backTracking (String s,int start,int ip) { if (start==s.length()&&ip==4 ){ res.add(sb.toString()); return ; } if (start==s.length()||ip==4 ){ return ; } for (int i=start;i<s.length()&&i-start<3 &&Integer.parseInt(s.substring(start,i+1 ))>=0 &&Integer.parseInt(s.substring(start,i+1 ))<=255 ;i++){ if (i-start>=1 &&s.charAt(start)=='0' ){ break ; } sb.append(s.substring(start,i+1 )); if (ip<3 ){ sb.append("." ); } ip++; backTracking(s,i+1 ,ip); ip--; sb.delete(start+ip,i+ip+2 ); } } }
子集 leetcode题目链接
给定一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。可以按 任意顺序 返回解集。
思路草稿
题解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { List<Integer> path = new ArrayList <>(); List<List<Integer>> res = new ArrayList <>(); public List<List<Integer>> subsets (int [] nums) { backTracking(nums,0 ); return res; } private void backTracking (int [] nums,int start) { res.add(new ArrayList <>(path)); if (start>=nums.length){ return ; } for (int i = start;i<nums.length;i++){ path.add(nums[i]); backTracking(nums,i+1 ); path.remove(path.size()-1 ); } } }
子集II leetcode题目链接
给定一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
思路草稿
题解 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 31 32 class Solution { List<Integer> path = new ArrayList <>(); List<List<Integer>> res = new ArrayList <>(); boolean [] used; public List<List<Integer>> subsetsWithDup (int [] nums) { used = new boolean [nums.length]; Arrays.sort(nums); backTracking(nums,0 ); return res; } private void backTracking (int [] nums,int start) { res.add(new ArrayList <>(path)); if (start>=nums.length){ return ; } for (int i = start;i<nums.length;i++){ if (i>0 &&nums[i]==nums[i-1 ]&&!used[i-1 ]){ continue ; } path.add(nums[i]); used[i] = true ; backTracking(nums,i+1 ); path.remove(path.size()-1 ); used[i] = false ; } } }
递增子序列 leetcode题目链接
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。
示例:
输入: [4, 6, 7, 7]
输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
思路草稿
题解 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 31 32 class Solution { List<Integer> path = new ArrayList <>(); List<List<Integer>> res = new ArrayList <>(); public List<List<Integer>> findSubsequences (int [] nums) { backTracking(nums,0 ); return res; } private void backTracking (int [] nums,int start) { if (path.size()>1 ){ res.add(new ArrayList <>(path)); } if (start>=nums.length){ return ; } boolean [] used = new boolean [201 ]; for (int i = start;i<nums.length;i++){ if (!path.isEmpty()&&nums[i]<path.get(path.size()-1 )||used[nums[i]+100 ]==true ){ continue ; } used[nums[i]+100 ]=true ; path.add(nums[i]); backTracking(nums,i+1 ); path.remove(path.size()-1 ); } } }
全排列 leetcode题目链接
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
思路草稿
题解 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 class Solution { List<Integer> path = new ArrayList <>(); List<List<Integer>> res = new ArrayList <>(); boolean [] used; public List<List<Integer>> permute (int [] nums) { used = new boolean [nums.length]; backTracking(nums); return res; } private void backTracking (int [] nums) { if (path.size()==nums.length){ res.add(new ArrayList <>(path)); return ; } for (int i = 0 ;i<nums.length;i++){ if (used[i]==true ){ continue ; } used[i]=true ; path.add(nums[i]); backTracking(nums); path.remove(path.size()-1 ); used[i]=false ; } } }
全排列II leetcode题目链接
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
思路草稿
去重时:used[i-1]==true 为树枝去重,used[i-1]==false 为树层去重
题解 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 31 32 33 34 35 36 37 class Solution { List<Integer> path = new ArrayList <>(); List<List<Integer>> res = new ArrayList <>(); boolean [] used; public List<List<Integer>> permuteUnique (int [] nums) { if (nums==null ||nums.length==0 ){ return res; } Arrays.sort(nums); used = new boolean [nums.length]; backTracking(nums); return res; } private void backTracking (int [] nums) { if (path.size()==nums.length){ res.add(new ArrayList <>(path)); return ; } for (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.add(nums[i]); backTracking(nums); path.remove(path.size()-1 ); used[i]=false ; } } }
重新安排行程 leetcode题目链接
给定一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请对该行程进行重新规划排序。
所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请按字典排序返回最小的行程组合。
例如,行程 [“JFK”, “LGA”] 与 [“JFK”, “LGB”] 相比就更小,排序更靠前。 假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。
思路草稿
题解 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 31 32 33 34 35 36 37 38 class Solution { List<String> res = new ArrayList <>(); boolean [] used; public List<String> findItinerary (List<List<String>> tickets) { Collections.sort(tickets,(a,b)->a.get(1 ).compareTo(b.get(1 ))); used = new boolean [tickets.size()]; res.add("JFK" ); backTracking(tickets); return res; } private boolean backTracking (List<List<String>> tickets) { if (res.size()==tickets.size()+1 ){ return true ; } for (int i = 0 ;i<tickets.size();i++){ if (used[i]==true ){ continue ; } if (tickets.get(i).get(0 ).equals(res.get(res.size()-1 ))){ used[i]=true ; res.add(tickets.get(i).get(1 )); if (backTracking(tickets)){ return true ; } res.remove(res.size()-1 ); used[i]=false ; } } return false ; } }
N皇后 leetcode题目链接
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给定一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
思路草稿
题解 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 class Solution { List<List<String>> res = new ArrayList <>(); public List<List<String>> solveNQueens (int n) { char [][] chessboard = new char [n][n]; for (char [] c : chessboard){ Arrays.fill(c,'.' ); } backTracking(chessboard,n,0 ); return res; } private void backTracking (char [][] chessboard,int n,int row) { if (row==n){ res.add(Array2List(chessboard)); return ; } for (int col = 0 ;col<n;col++){ if (isVaild(chessboard,n,row,col)){ chessboard[row][col]='Q' ; row++; backTracking(chessboard,n,row); row--; chessboard[row][col]='.' ; } } } private List Array2List (char [][] chessboard) { List<String> list = new ArrayList <>(); for (char [] c:chessboard){ list.add(String.copyValueOf(c)); } return list; } private boolean isVaild (char [][] chessboard,int n,int row,int col) { for (int i = 0 ;i<row;i++){ if (chessboard[i][col]=='Q' ){ return false ; } } for (int i = row - 1 ,j = col - 1 ;i>=0 &&j>=0 ;i--,j--){ if (chessboard[i][j]=='Q' ){ return false ; } } for (int i = row - 1 ,j = col + 1 ;i>=0 &&j<n;i--,j++){ if (chessboard[i][j]=='Q' ){ return false ; } } return true ; } }
解数独 leetcode题目链接
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图) 数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
题解 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 class Solution { public void solveSudoku (char [][] board) { solveSudokuHelper(board); } private boolean solveSudokuHelper (char [][] board) { for (int i = 0 ; i < 9 ; i++){ for (int j = 0 ; j < 9 ; j++){ if (board[i][j] != '.' ){ continue ; } for (char k = '1' ; k <= '9' ; k++){ if (isValidSudoku(i, j, k, board)){ board[i][j] = k; if (solveSudokuHelper(board)){ return true ; } board[i][j] = '.' ; } } return false ; } } return true ; } private boolean isValidSudoku (int row, int col, char val, char [][] board) { for (int i = 0 ; i < 9 ; i++){ if (board[row][i] == val){ return false ; } } for (int j = 0 ; j < 9 ; j++){ if (board[j][col] == val){ return false ; } } int startRow = (row / 3 ) * 3 ; int startCol = (col / 3 ) * 3 ; for (int i = startRow; i < startRow + 3 ; i++){ for (int j = startCol; j < startCol + 3 ; j++){ if (board[i][j] == val){ return false ; } } } return true ; } }