组合问题
第77题. 组合
题⽬链接:https://leetcode-cn.com/problems/combinations/
给定两个整数 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> path = new ArrayList<>(); LinkedList增添的时间复杂度更低
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
// backtraking(n, k, 0); 返回范围 [1, n] 中所有可能的 k 个数的组合。所以index从1开始
backtraking(n, k, 1);
return result;
}
void backtraking(int n, int k, int index) {
if(path.size() == k) {
// result.add(path); new ArrayList<>() 将LinkedList类型的path转换为ArrayList类型
result.add(new ArrayList<>(path));
return;
}
for(int i = index; i <= n; i++){
path.add(i);
// backtraking(n, k , index + 1);
backtraking(n, k ,i + 1);
path.remove(path.size() - 1);
// path.removeLast();
}
}
}
第216题.组合总和III
链接:https://leetcode-cn.com/problems/combination-sum-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<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum3(int k, int n) {
backtracking(k, n, 0, 1);
return result;
}
void backtracking(int k, int n,int sum, int index) {
if(sum == n && path.size() == k) {
result.add(new ArrayList<>(path));
return;
}
for(int i = index; i <= 9 && n >= sum + i; i++) {
path.add(i);
sum += i;
backtracking(k, n, sum, i + 1);
sum -= i;
path.remove(path.size() - 1);
}
}
}
17.电话号码的字母组合
题⽬链接:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/
给定⼀个仅包含数字 2-9 的字符串,返回所有它能表⽰的字母组合。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
⽰例: 输⼊:"23" 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
说明:尽管上⾯的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。
class Solution {
String[] letterMap = new String[]{
"",//0
"",//1
"abc",//2
"def",//3
"ghi",//4
"jkl",//5
"mno",//6
"pqrs",//7
"tuv",//8
"wxyz"//9
};
List<String> result = new ArrayList<>();
StringBuilder path = new StringBuilder();
public List<String> letterCombinations(String digits) {
if(digits == null || digits.length() == 0) return result;
backTracking(digits, 0);
return result;
}
void backTracking(String digits, int index) {
if(index == digits.length()) {
result.add(path.toString());
return;
}
String s = letterMap[digits.charAt(index) - '0'];
for(int i = 0; i < s.length(); i++){ //对每个按键的水平遍历
path.append(s.charAt(i));
backTracking(digits, index + 1); //对不同按键的深度递归
path.deleteCharAt(path.length() - 1);
}
}
}
39. 组合总和
题⽬链接:https://leetcode-cn.com/problems/combination-sum/
给定⼀个⽆重复元素的数组 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] ]
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList path = new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
backtracking(candidates, target, 0, 0);
return result;
}
void backtracking(int[] candidates, int target, int sum, int index) {
if(target == sum) {
// result.add(path);
result.add(new ArrayList<>(path));
return;
}
for(int i = index; i < candidates.length && target >= sum + i; i++){ //target >= sum + i要提前排序
path.add(candidates[i]);
sum += candidates[i];
backtracking(candidates, target, sum, i);
sum -= candidates[i];
path.remove(path.size() - 1);
}
}
}
40.组合总和II
题⽬链接:https://leetcode-cn.com/problems/combination-sum-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 {
// public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> result = new ArrayList<>();
LinkedList path = new LinkedList<>();
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 result;
}
void backtracking(int[] candidates, int target, int sum, int index) {
if(target == sum) {
// result.add(path);
result.add(new ArrayList<>(path));
return;
}
// for(int i = index; i < candidates.length && target >= sum + i; i++){ //target >= sum + i要提前排序
for(int i = index; i < candidates.length && target >= sum + candidates[i]; i++){
// if(i > 0 && candidates[i-1] == candidates[i] && used[i - 1] == false){
if(i > 0 && candidates[i-1] == candidates[i] && used[i - 1] == false){
continue;
}
path.add(candidates[i]);
sum += candidates[i];
used[i] = true;
backtracking(candidates, target, sum, i + 1);
used[i] = false;
sum -= candidates[i];
path.remove(path.size() - 1);
}
}
}
分割问题
131.分割回⽂串
题⽬链接:https://leetcode-cn.com/problems/palindrome-partitioning/
给定⼀个字符串 s,将 s 分割成⼀些⼦串,使每个⼦串都是回⽂串。 返回 s 所有可能的分割⽅案。
⽰例: 输⼊: "aab" 输出: [ ["aa","b"], ["a","a","b"] ]
class Solution {
List<List<String>> result = new ArrayList<>();
LinkedList path = new LinkedList<>();
public List<List<String>> partition(String s) {
backtracking(s, 0);
return result;
}
void backtracking(String s, int index) {
if(index == s.length()) {
// result.add(path);
result.add(new ArrayList(path));
return;
}
for(int i = index; i < s.length(); i++) {
if(isvalid(s, index ,i)) {
// path.add(s.subStr(i, index));
// path.add(s.substring(i+1, index));
path.add(s.substring(index, i+1));
backtracking(s, i+1);
path.removeLast();
}else {
continue;
}
}
}
boolean isvalid(String s, int startIndex, int end) {
for (int i = startIndex, j = end; i < j; i++, j--) {
if (s.charAt(i) != s.charAt(j)) {
return false;
}
}
return true;
}
}
子集问题
第78题. ⼦集
题⽬地址:https://leetcode-cn.com/problems/subsets/
给定⼀组不含重复元素的整数数组 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<>();
LinkedList path = new LinkedList<>();
public List<List<Integer>> subsets(int[] nums) {
backStraking(nums, 0);
return result;
}
void backStraking(int[] nums, int index) {
result.add(new ArrayList<>(path));
if(index >= nums.length) {
return;
}
for(int i = index; i < nums.length; i++) {
path.add(nums[i]);
// backStraking(nums, index +1); 怎么能用index呢?!
backStraking(nums, i +1);
path.removeLast();
}
}
}
491.递增⼦序列
题⽬链接:https://leetcode-cn.com/problems/increasing-subsequences/
给定⼀个整型数组, 你的任务是找到所有该数组的递增⼦序列,递增⼦序列的长度⾄少是 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]]
说明: 给定数组的长度不会超过15。
· 数组中的整数范围是 [-100,100]。
给定数组中可能包含重复数字,相等的数字应该被视为递增的⼀种情况。
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtracking(nums,0);
return result;
}
void backtracking(int[] nums,int index) {
if(path.size() > 1) { //递增子序列长度至少为2
result.add(new ArrayList<>(path));
}
if(index == nums.length){
return;
}
boolean[] used = new boolean[201]; //同一层相同结点不能重复
for(int i = index; i < nums.length; i++) {
//纵向判断递增
if(path.size() > 0 && nums[i] < path.get(path.size() - 1)) {
continue;
}
//横向判断元素是否重复(序列无序)
if(used[nums[i] + 100] == true) {
continue;
}
// used[nums[i]] = true; 因为下标没有负数
used[nums[i] + 100] = true;
path.add(nums[i]);
backtracking(nums, i + 1);
path.removeLast();
}
}
}
排列问题
46.全排列
题⽬链接:https://leetcode-cn.com/problems/permutations/
给定⼀个 没有重复 数字的序列,返回其所有可能的全排列。
⽰例: 输⼊: [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 ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> permute(int[] nums) {
boolean[] used = new boolean[nums.length + 1];
Arrays.fill(used, false);
backtracking(nums,used);
return result;
}
//同一树枝标记用过 used[i] == true
//将used作为参数传递 比较的是同一树枝和同一树层
//将used放在for循环上面,比较的是for循环是否有重复元素
void backtracking(int[] nums, boolean[] used) {
if(path.size() == nums.length) {
result.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, used);
path.remove(path.size() - 1);
used[i] = false;
}
}
}
47.全排列 II
题⽬链接:https://leetcode-cn.com/problems/permutations-ii/
给定⼀个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
⽰例 1: 输⼊:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]]
⽰例 2: 输⼊:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
提⽰: 1 <= nums.length <= 8 -10 <= nums[i] <= 10
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
int len;
public List<List<Integer>> permuteUnique(int[] nums) {
len = nums.length;
boolean[] used = new boolean[nums.length + 1]; //[]是长度 <>是范型 ()是内容
Arrays.fill(used, false);
Arrays.sort(nums);
backTracking(nums,used);
return result;
}
void backTracking(int[] nums, boolean[] used){
if(path.size() == nums.length) {
result.add(new ArrayList<>(path));
return;
}
for(int i = 0; i < len; i++) {
//纵向判断
if(used[i] == true) continue;
//横向判断 原序列需要排序
if(i > 0 && nums[i] == nums[i-1] && used[i-1] == false) continue;
used[i] = true;
path.add(nums[i]);
backTracking(nums, used);
path.removeLast();
used[i] = false;
}
}
}
棋盘问题
第51题. N皇后
题⽬链接: https://leetcode-cn.com/problems/n-queens/ n
皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
8 皇后问题的⼀种解法:
给定⼀个整数 n,返回所有不同的 n 皇后问题的解决⽅案。 每⼀种解法包含⼀个明确的 n 皇后问题的棋⼦放置⽅案,该⽅案中 'Q' 和 '.' 分别代表了皇后 和空位。
⽰例: 输⼊: 4 输出: [ [".Q..", "...Q", "Q...", "..Q."],["..Q.", "Q...", "...Q", ".Q.."] ] 解释: 4 皇后问题存在两个不同的解法。
class Solution {
List<List<String>> result = new ArrayList<>();
public List<List<String>> solveNQueens(int n) {
char[][] chessboard = new char[n][n];
for(char[] c : chessboard) {
Arrays.fill(c,'.');
}
backtracking(n, 0, chessboard);
return result;
}
void backtracking(int n, int row, char[][] chessboard){
if(row == n) {
result.add(Array2List(chessboard));
return;
}
for(int col = 0; col < n; col++) { //行和列都是从0开始
if(isvalid(n,row,col,chessboard)) {
chessboard[row][col] = 'Q';
backtracking(n, row + 1, chessboard);
chessboard[row][col] = '.';
}
}
}
public List<String> Array2List(char[][] chessboard) {
List<String> list = new ArrayList<>();
for (char[] c : chessboard) {
list.add(String.copyValueOf(c));
}
return list;
}
public boolean isvalid(int n, int row, int col, char[][] chessboard) {
// 检查列
for (int i=0; i<n; ++i) {
if (chessboard[i][col] == 'Q') {
return false;
}
}
// 检查45度对角线
for (int i=row-1, j=col-1; i>=0 && j>=0; i--, j--) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
// 检查135度对角线
for (int i=row-1, j=col+1; i>=0 && j<=n-1; i--, j++) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
return true;
}
}