回溯算法习题集
易错点
- 遇到元素重复的题,一定要记住对数组排序
Arrays.sort(nums)
- 遇到求和target的题目,要在判断条件中加入if(trackSum > target)
- 当遇到多个集合的问题时,只需要递归加嵌套循环即可,电话号码以及N皇后均是此类问题。
- 括号生成问题关键:[1,i]中左括号数量 >= 右括号数量
1. leetcode-77:组合
题目:给定两个整数 n 和 k,返回范围[1, n]中所有可能的k个数的组合。
class Solution {
List<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
backtrack(n,1,k);
return res;
}
void backtrack(int n, int start, int k) {
if(track.size() == k) {
res.add(new LinkedList<>(track));
return;
}
for(int i=start; i<=n-(k-track.size())+1; i++) {
track.add(i);
backtrack(n,i+1,k);
track.removeLast();
}
}
}
2. leetcode-216:组合III
题目:找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:(1)、只使用数字1到9 (2)、每个数字最多使用一次
class Solution {
List<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
int trackSum = 0;
public List<List<Integer>> combinationSum3(int k, int n) {
backtrack(k,1,n);
return res;
}
void backtrack(int k, int start, int n) {
if(trackSum > n) {
return;
}
if(track.size() == k && trackSum == n) {
res.add(new LinkedList<>(track));
return;
}
for(int i=start; i<=9-(k-track.size())+1; i++) {
trackSum += i;
track.add(i);
backtrack(k,i+1,n);
track.removeLast();
trackSum -= i;
}
}
}
3. leetcode-39:组合之和
题目:给你一个无重复元素的整数数组 candidates 和一个目标整数target ,找出 candidates中可以使数字和为目标数target的所有不同组合,并以列表形式返回。candidates中的同一个数字可以无限制重复被选取。如果至少一个数字的被选数量不同,则两种组合是不同的。
class Solution {
List<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
int trackSum = 0;
public List<List<Integer>> combinationSum(int[] candidates, int target) {
backtrack(candidates,target,0);
return res;
}
void backtrack(int[] candidates, int target, int start) {
if(trackSum == target) {
res.add(new LinkedList<>(track));
return;
}
if(trackSum > target) {
return;
}
for(int i=start; i<candidates.length; i++) {
trackSum += candidates[i];
track.add(candidates[i]);
backtrack(candidates,target,i);
track.removeLast();
trackSum -= candidates[i];
}
}
}
4. leetcode-40:组合II
题目:给定一个候选人编号的集合candidates和一个目标数 target ,找出candidates中所有可以使数字和为target的组合。candidates中的每个数字在每个组合中只能使用一次。
class Solution {
List<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
int trackSum = 0;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
backtrack(candidates,target,0);
return res;
}
void backtrack(int[] candidates, int target, int start) {
if(trackSum == target) {
res.add(new LinkedList<>(track));
return;
}
if(trackSum > target) {
return;
}
for(int i=start; i<candidates.length; i++) {
if(i > start && candidates[i] == candidates[i-1]) {
continue;
}
trackSum += candidates[i];
track.add(candidates[i]);
backtrack(candidates,target,i+1);
trackSum -= candidates[i];
track.removeLast();
}
}
}
5. leetcode-78:子集
题目:给你一个整数数组nums,数组中的元素互不相同。返回该数组所有可能的子集(幂集)。
class Solution {
List<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
public List<List<Integer>> subsets(int[] nums) {
backtrack(nums,0);
return res;
}
void backtrack(int[] nums, int start) {
res.add(new LinkedList<>(track));
for(int i=start; i<nums.length; i++) {
track.add(nums[i]);
backtrack(nums,i+1);
track.removeLast();
}
}
}
6. leetcode-90:子集II
题目:给你一个整数数组nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。解集不能包含重复的子集
class Solution {
List<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
backtrack(nums,0);
return res;
}
void backtrack(int[] nums, int start) {
res.add(new LinkedList<>(track));
for(int i=start; i<nums.length; i++) {
if(i > start && nums[i] == nums[i-1]) {
continue;
}
track.add(nums[i]);
backtrack(nums,i+1);
track.removeLast();
}
}
}
7. leetcode-46:全排列
题目:给定一个不含重复数字的数组nums,返回其所有可能的全排列。
class Solution {
List<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
boolean[] used;
public List<List<Integer>> permute(int[] nums) {
used = new boolean[nums.length];
backtrack(nums);
return res;
}
void backtrack(int[] nums) {
if(track.size() == nums.length) {
res.add(new LinkedList<>(track));
return;
}
for(int i=0; i<nums.length; i++) {
if(used[i]) {
continue;
}
used[i] = true;
track.add(nums[i]);
backtrack(nums);
track.removeLast();
used[i] = false;
}
}
}
8. leetcode-47:全排列II
题目:给定一个可包含重复数字的序列nums,按任意顺序返回所有不重复的全排列。
class Solution {
List<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
boolean[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
Arrays.sort(nums);
used = new boolean[nums.length];
backtrack(nums);
return res;
}
void backtrack(int[] nums) {
if(track.size() == nums.length) {
res.add(new LinkedList<>(track));
return;
}
for(int i=0; i<nums.length; i++) {
if(used[i]) {
continue;
}
if(i > 0 && nums[i] == nums[i-1] && !used[i-1]) {
continue;
}
used[i] = true;
track.add(nums[i]);
backtrack(nums);
track.removeLast();
used[i] = false;
}
}
}
9. leetcode-17:电话号码的字母组合
题目:给定一个仅包含数字2-9的字符串,返回所有它能表示的字母组合。
class Solution {
List<String> res = new LinkedList<>();
String[] mapping = new String[]{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public List<String> letterCombinations(String digits) {
if(digits.isEmpty()) {
return res;
}
backtrack(digits,0,new StringBuilder());
return res;
}
void backtrack(String digits, int start, StringBuilder sb) {
if(sb.length() == digits.length()) {
res.add(sb.toString());
return;
}
for(int i=start; i<digits.length(); i++) {
int digit = digits.charAt(i) - '0';
for(char c : mapping[digit].toCharArray()) {
sb.append(c);
backtrack(digits,i+1,sb);
sb.deleteCharAt(sb.length()-1);
}
}
}
}
另一种思路:
class Solution {
List<String> res = new LinkedList<>();//结果集
String[] mapping = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
StringBuilder sb = new StringBuilder();//路径集
public List<String> letterCombinations(String digits) {
if(digits.isEmpty()) {
return res;
}
backtrack(digits,0);
return res;
}
void backtrack(String digits, int row) {
if(sb.length() == digits.length()) {
res.add(sb.toString());
return;
}
//第一行循环,第二行循环...
String str = mapping[digits.charAt(row) - '0'];
for(int i=0; i<str.length(); i++) {
sb.append(str.charAt(i));
backtrack(digits,row+1);
sb.deleteCharAt(sb.length()-1);
}
}
}
10. leetcode-491:递增子序列
题目:给你一个整数数组nums,找出并返回所有该数组中不同的递增子序列,递增子序列中至少有两个元素 。你可以按任意顺序返回答案。数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
class Solution {
List<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtack(nums,0);
return res;
}
void backtack(int[] nums, int start) {
Set<Integer> used = new HashSet<>();
if(track.size() > 1) {
res.add(new LinkedList<>(track));
}
for(int i=start; i<nums.length; i++) {
//相当于排序+nums[i]==nums[i-1]
if(used.contains(nums[i])){
continue;
}
//递增判断逻辑
if(!track.isEmpty() && nums[i] < track.getLast()) {
continue;
}
used.add(nums[i]);
track.add(nums[i]);
backtack(nums,i+1);
track.removeLast();
}
}
}
11. leetcode-51:N皇后
题目:n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
class Solution {
//相当于两个集合组合在一起
List<List<String>> res = new LinkedList<>();
LinkedList<String> track = new LinkedList<>();
public List<List<String>> solveNQueens(int n) {
char[][] board = new char[n][n];
for(int i=0; i<n; i++) {
//填充每行
Arrays.fill(board[i],'.');
}
backtrack(board,0);
return res;
}
void backtrack(char[][] board, int row) {
if(track.size() == board.length) {
res.add(new LinkedList<>(track));
return;
}
for(int i=0; i<board.length; i++) {
//判断皇后是否相互攻击
if(!isValid(board,row,i)) {
continue;
}
board[row][i] = 'Q';
track.add(new String(board[row]));
backtrack(board,row+1);
track.removeLast();
board[row][i] = '.';
}
}
boolean isValid(char[][] board, int row, int col) {
int n = board.length;
for(int i=0; i<n; i++) {
if(board[i][col] == 'Q') return false;
}
for(int i=row-1,j=col-1; i>=0 && j>=0; i--,j--) {
if(board[i][j] == 'Q') return false;
}
for(int i=row-1,j=col+1; i>=0 && j<n; i--,j++) {
if(board[i][j] == 'Q') return false;
}
return true;
}
}
12. leetcode-37:解数独
题目:编写一个程序,通过填充空格来解决数独问题。
class Solution {
public void solveSudoku(char[][] board) {
backtrack(board);
}
boolean backtrack(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(!isValid(board,i,j,k)) { //(i,j)位置放k是否合适
continue;
}
board[i][j] = k;
if(backtrack(board)) return true; //一个位置找到一个合适的数字立马返回
board[i][j] = '.';
}
return false; //9个数都试完了,没有true就返回false
}
}
return true; //所有的都试完了,没有返回false,就返回true
}
boolean isValid(char[][] board, int row, int col, char n) {
for(int i=0; i<9; i++) {
if(board[row][i] == n) return false;
}
for(int i=0; i<9; i++) {
if(board[i][col] == n) return false;
}
int rowStart = (row/3)*3;
int colStart = (col/3)*3;
for(int i=rowStart; i<rowStart+3; i++) {
for(int j=colStart; j<colStart+3; j++) {
if(board[i][j] == n) return false;
}
}
return true;
}
}
13. leetcode-22:括号生成
题目:数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。
class Solution {
List<String> res = new LinkedList<>();
StringBuilder track = new StringBuilder();
public List<String> generateParenthesis(int n) {
backtrack(n,n);
return res;
}
void backtrack(int left, int right) { //left为左括号剩余数量,right为右括号剩余数量,
if(left < 0 ||right < 0) {
return;
}
if(left > right) { //[0,i]中左括号的数量肯定大于等于右括号
return;
}
if(left == 0 && right ==0) {
res.add(track.toString());
return;
}
track.append('(');
backtrack(left-1,right);
track.deleteCharAt(track.length()-1);
track.append(')');
backtrack(left,right-1);
track.deleteCharAt(track.length()-1);
}
}
声明:转载自力扣网