3.28 图论+回溯
This commit is contained in:
parent
5dfb8008da
commit
c85a5023f1
49
src/main/java/backtrack/CombinationSum.java
Normal file
49
src/main/java/backtrack/CombinationSum.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package backtrack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目: 39. 组合总和 (combinationSum)
|
||||||
|
* 描述:给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
|
||||||
|
* candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
|
||||||
|
* 对于给定的输入,保证和为 target 的不同组合数少于 150 个。
|
||||||
|
|
||||||
|
示例 1:
|
||||||
|
输入:candidates = [2,3,6,7], target = 7
|
||||||
|
输出:[[2,2,3],[7]]
|
||||||
|
解释:
|
||||||
|
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
|
||||||
|
7 也是一个候选, 7 = 7 。
|
||||||
|
仅有这两种组合。
|
||||||
|
|
||||||
|
示例 2:
|
||||||
|
输入: candidates = [2,3,5], target = 8
|
||||||
|
输出: [[2,2,2,2],[2,3,3],[3,5]]
|
||||||
|
|
||||||
|
* 链接:https://leetcode.cn/problems/combination-sum/
|
||||||
|
*/
|
||||||
|
public class CombinationSum {
|
||||||
|
void dfs(int[] candidates,List<Integer>path,List<List<Integer>>res,int start,int cur,int target){
|
||||||
|
if(cur==target) {
|
||||||
|
res.add(new ArrayList<>(path));
|
||||||
|
}
|
||||||
|
else if(cur>target)
|
||||||
|
return;
|
||||||
|
else {
|
||||||
|
for (int i = start; i < candidates.length; i++) {
|
||||||
|
path.add(candidates[i]);
|
||||||
|
cur+=candidates[i];
|
||||||
|
dfs(candidates,path,res,i,cur,target);
|
||||||
|
cur-=candidates[i];
|
||||||
|
path.remove(path.size()-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public List<List<Integer>> combinationSum(int[] candidates, int target) {
|
||||||
|
List<List<Integer>>res=new ArrayList<>();
|
||||||
|
List<Integer>path=new ArrayList<>();
|
||||||
|
dfs(candidates,path,res,0,0,target);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
59
src/main/java/backtrack/CombinationSum2.java
Normal file
59
src/main/java/backtrack/CombinationSum2.java
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package backtrack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目: 40. 组合总和 II (combinationSum2)
|
||||||
|
* 描述:给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
|
||||||
|
* candidates 中的每个数字在每个组合中只能使用 一次 。
|
||||||
|
* 注意:解集不能包含重复的组合。
|
||||||
|
|
||||||
|
示例 1:
|
||||||
|
输入: candidates = [10,1,2,7,6,1,5], target = 8,
|
||||||
|
输出:
|
||||||
|
[[1,1,6],
|
||||||
|
[1,2,5],
|
||||||
|
[1,7],
|
||||||
|
[2,6]]
|
||||||
|
|
||||||
|
示例 2:
|
||||||
|
输入: candidates = [2,5,2,1,2], target = 5,
|
||||||
|
输出:
|
||||||
|
[[1,2,2],
|
||||||
|
[5]]
|
||||||
|
|
||||||
|
* 链接:https://leetcode.cn/problems/combination-sum-ii/
|
||||||
|
*/
|
||||||
|
public class CombinationSum2 {
|
||||||
|
void backtrack(List<List<Integer>>res,List<Integer>path,int[] candidates,int[]visited,int start,int cur,int target){
|
||||||
|
if(cur==target) {
|
||||||
|
res.add(new ArrayList<>(path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = start; i < candidates.length; i++) {
|
||||||
|
if(i>0 &&candidates[i]==candidates[i-1]&&visited[i-1]==0)
|
||||||
|
continue;
|
||||||
|
if(visited[i]==0) {
|
||||||
|
cur += candidates[i];
|
||||||
|
if(cur>target)
|
||||||
|
return;
|
||||||
|
path.add(candidates[i]);
|
||||||
|
visited[i] = 1;
|
||||||
|
backtrack(res, path, candidates, visited, i + 1, cur, target);
|
||||||
|
path.remove(path.size() - 1);
|
||||||
|
visited[i] = 0;
|
||||||
|
cur -= candidates[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
|
||||||
|
List<List<Integer>>res=new ArrayList<>();
|
||||||
|
List<Integer>path=new ArrayList<>();
|
||||||
|
int[] visited=new int[candidates.length];
|
||||||
|
Arrays.sort(candidates);
|
||||||
|
backtrack(res,path,candidates,visited,0,0,target);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
43
src/main/java/backtrack/Combine.java
Normal file
43
src/main/java/backtrack/Combine.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package backtrack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目: 77. 组合 (combine)
|
||||||
|
* 描述:给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
|
||||||
|
* 你可以按 任何顺序 返回答案。
|
||||||
|
|
||||||
|
示例 1:
|
||||||
|
输入:n = 4, k = 2
|
||||||
|
输出:
|
||||||
|
[[2,4],
|
||||||
|
[3,4],
|
||||||
|
[2,3],
|
||||||
|
[1,2],
|
||||||
|
[1,3],
|
||||||
|
[1,4],]
|
||||||
|
|
||||||
|
* 链接:https://leetcode.cn/problems/combinations/
|
||||||
|
*/
|
||||||
|
public class Combine {
|
||||||
|
void backtrack(List<List<Integer>> res,List<Integer>path,int n,int start,int cur,int k){
|
||||||
|
if(cur==k){
|
||||||
|
res.add(new ArrayList<>(path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = start; i <= n; i++) {
|
||||||
|
path.add(i);
|
||||||
|
cur++;
|
||||||
|
backtrack(res,path,n,i+1,cur,k);
|
||||||
|
cur--;
|
||||||
|
path.remove(path.size()-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public List<List<Integer>> combine(int n, int k) {
|
||||||
|
List<List<Integer>> res=new ArrayList<>();
|
||||||
|
List<Integer>path=new ArrayList<>();
|
||||||
|
backtrack(res,path,n,1,0,k);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
56
src/main/java/backtrack/Exist.java
Normal file
56
src/main/java/backtrack/Exist.java
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package backtrack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目: 79. 单词搜索 (Exist)
|
||||||
|
* 描述:给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
|
||||||
|
* 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
|
||||||
|
|
||||||
|
示例 1:
|
||||||
|
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
|
||||||
|
输出:true
|
||||||
|
|
||||||
|
示例 2:
|
||||||
|
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
|
||||||
|
输出:true
|
||||||
|
|
||||||
|
* 链接:https://leetcode.cn/problems/word-search/
|
||||||
|
*/
|
||||||
|
public class Exist {
|
||||||
|
boolean flag = false;
|
||||||
|
|
||||||
|
void dfs(char[][] board, String word, int i, int j, int index) {
|
||||||
|
if (index == word.length()) {
|
||||||
|
flag = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int r = board.length, c = board[0].length;
|
||||||
|
// 检查边界和是否匹配字符
|
||||||
|
if (i < 0 || i >= r || j < 0 || j >= c || board[i][j] != word.charAt(index)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 记录当前字符并标记为已访问(这里用 '#' 标记已访问,可以用其他不可能出现在 board 中的字符)
|
||||||
|
char temp = board[i][j];
|
||||||
|
board[i][j] = '#';
|
||||||
|
|
||||||
|
// 向四个方向递归
|
||||||
|
dfs(board, word, i - 1, j, index + 1);
|
||||||
|
dfs(board, word, i + 1, j, index + 1);
|
||||||
|
dfs(board, word, i, j - 1, index + 1);
|
||||||
|
dfs(board, word, i, j + 1, index + 1);
|
||||||
|
|
||||||
|
// 回溯还原
|
||||||
|
board[i][j] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean exist(char[][] board, String word) {
|
||||||
|
for (int i = 0; i < board.length; i++) {
|
||||||
|
for (int j = 0; j < board[0].length; j++) {
|
||||||
|
if (board[i][j] == word.charAt(0)) {
|
||||||
|
dfs(board, word, i, j, 0);
|
||||||
|
if (flag) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
}
|
50
src/main/java/backtrack/GenerateParenthesis.java
Normal file
50
src/main/java/backtrack/GenerateParenthesis.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package backtrack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目: 22. 括号生成 (generateParenthesis)
|
||||||
|
* 描述:数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
|
||||||
|
|
||||||
|
示例 1:
|
||||||
|
输入:n = 3
|
||||||
|
输出:["((()))","(()())","(())()","()(())","()()()"]
|
||||||
|
|
||||||
|
示例 2:
|
||||||
|
输入:n = 1
|
||||||
|
输出:["()"]
|
||||||
|
|
||||||
|
* 链接:https://leetcode.cn/problems/generate-parentheses/
|
||||||
|
*/
|
||||||
|
//做出来了,但我的写法很丑,改为标准答案
|
||||||
|
public class GenerateParenthesis {
|
||||||
|
// 回溯函数
|
||||||
|
void dfs(List<String> res, StringBuilder path, int left, int right, int n) {
|
||||||
|
// 如果左右括号都用完了,加入结果
|
||||||
|
if (left == n && right == n) {
|
||||||
|
res.add(path.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试加左括号
|
||||||
|
if (left < n) {
|
||||||
|
path.append('(');
|
||||||
|
dfs(res, path, left + 1, right, n);
|
||||||
|
path.deleteCharAt(path.length() - 1); // 回溯
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试加右括号(右括号不能多于左括号)
|
||||||
|
if (right < left) {
|
||||||
|
path.append(')');
|
||||||
|
dfs(res, path, left, right + 1, n);
|
||||||
|
path.deleteCharAt(path.length() - 1); // 回溯
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> generateParenthesis(int n) {
|
||||||
|
List<String> res = new ArrayList<>();
|
||||||
|
dfs(res, new StringBuilder(), 0, 0, n);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
57
src/main/java/backtrack/LetterCombinations.java
Normal file
57
src/main/java/backtrack/LetterCombinations.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package backtrack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目: 17. 电话号码的字母组合 (letterCombinations)
|
||||||
|
* 描述:给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
|
||||||
|
* 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
|
||||||
|
*
|
||||||
|
示例 1:
|
||||||
|
输入:digits = "23"
|
||||||
|
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
|
||||||
|
|
||||||
|
示例 2:
|
||||||
|
输入:digits = ""
|
||||||
|
输出:[]
|
||||||
|
|
||||||
|
* 链接:https://leetcode.cn/problems/letter-combinations-of-a-phone-number/
|
||||||
|
*/
|
||||||
|
//重做一遍
|
||||||
|
public class LetterCombinations {
|
||||||
|
public List<String> letterCombinations(String digits) {
|
||||||
|
List<String> res = new ArrayList<>();
|
||||||
|
// 如果输入为空,直接返回空结果
|
||||||
|
if (digits == null || digits.length() == 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// 电话号码对应的字母映射,索引 0 对应数字 2,依此类推
|
||||||
|
String[] mapping = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
|
||||||
|
// 从索引 0 开始回溯
|
||||||
|
backtrack(digits, mapping, 0, new StringBuilder(), res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void backtrack(String digits, String[] mapping, int index, StringBuilder combination, List<String> res) {
|
||||||
|
// 当组合长度等于输入数字长度时,说明构造出一个完整组合
|
||||||
|
if (index == digits.length()) {
|
||||||
|
res.add(combination.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 当前数字
|
||||||
|
char digitChar = digits.charAt(index);
|
||||||
|
// 计算 mapping 数组中的索引,数字 '2' 对应索引 0
|
||||||
|
int mappingIndex = digitChar - '2';
|
||||||
|
String letters = mapping[mappingIndex];
|
||||||
|
// 遍历当前数字对应的每个字母
|
||||||
|
for (int i = 0; i < letters.length(); i++) {
|
||||||
|
// 选择当前字母,加入组合
|
||||||
|
combination.append(letters.charAt(i));
|
||||||
|
// 递归构造下一个数字的字母组合
|
||||||
|
backtrack(digits, mapping, index + 1, combination, res);
|
||||||
|
// 回溯:移除最后加入的字母,尝试其他可能性
|
||||||
|
combination.deleteCharAt(combination.length() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
src/main/java/backtrack/Partition.java
Normal file
61
src/main/java/backtrack/Partition.java
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package backtrack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目: 131. 分割回文串 (partition)
|
||||||
|
* 描述:给你一个字符串 s,请你将 s 分割成一些 子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
|
||||||
|
|
||||||
|
示例 1:
|
||||||
|
输入:s = "aab"
|
||||||
|
输出:[["a","a","b"],["aa","b"]]
|
||||||
|
|
||||||
|
示例 2:
|
||||||
|
输入:s = "a"
|
||||||
|
输出:[["a"]]
|
||||||
|
|
||||||
|
* 链接:https://leetcode.cn/problems/palindrome-partitioning/
|
||||||
|
*/
|
||||||
|
//不会
|
||||||
|
public class Partition {
|
||||||
|
/**
|
||||||
|
* 递归与回溯:
|
||||||
|
* 从字符串的起始位置开始,遍历所有可能的分割点,对于每个可能的子串,先判断它是否为回文串。
|
||||||
|
*
|
||||||
|
* 如果是回文串,则将其加入当前的分割方案,然后递归处理剩下的部分。
|
||||||
|
* 当走到字符串末尾时,说明当前方案已经完成,将该方案加入最终结果。
|
||||||
|
* 回溯时需要将上一步加入的子串移除,以便尝试其他分割方案。
|
||||||
|
*/
|
||||||
|
public List<List<String>> partition(String s) {
|
||||||
|
List<List<String>> result = new ArrayList<>();
|
||||||
|
backtrack(s, 0, new ArrayList<>(), result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void backtrack(String s, int start, List<String> current, List<List<String>> result) {
|
||||||
|
if (start == s.length()) {
|
||||||
|
result.add(new ArrayList<>(current));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = start; i < s.length(); i++) {
|
||||||
|
// 提取子串并判断是否为回文
|
||||||
|
String sub = s.substring(start, i + 1);
|
||||||
|
if (isPalindrome(sub)) {
|
||||||
|
current.add(sub);
|
||||||
|
backtrack(s, i + 1, current, result);
|
||||||
|
current.remove(current.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改后的回文判断方法,只接收一个字符串
|
||||||
|
private boolean isPalindrome(String s) {
|
||||||
|
for (int i = 0; i < s.length() / 2; i++) {
|
||||||
|
if (s.charAt(i) != s.charAt(s.length() - i - 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
56
src/main/java/backtrack/Permute.java
Normal file
56
src/main/java/backtrack/Permute.java
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package backtrack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目: 46. 全排列 (permute)
|
||||||
|
* 描述:给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
|
||||||
|
*示例 1:
|
||||||
|
* 输入:nums = [1,2,3]
|
||||||
|
* 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
|
||||||
|
*
|
||||||
|
* 示例 2:
|
||||||
|
* 输入:nums = [0,1]
|
||||||
|
* 输出:[[0,1],[1,0]]
|
||||||
|
*
|
||||||
|
* 示例 3:
|
||||||
|
* 输入:nums = [1]
|
||||||
|
* 输出:[[1]]
|
||||||
|
*
|
||||||
|
* 链接:https://leetcode.cn/problems/permutations/
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class Permute {
|
||||||
|
public List<List<Integer>> permute(int[] nums) {
|
||||||
|
List<List<Integer>> res = new ArrayList<>();
|
||||||
|
// 用来标记数组中数字是否被使用
|
||||||
|
boolean[] used = new boolean[nums.length];
|
||||||
|
List<Integer> path = new ArrayList<>();
|
||||||
|
backtrack(nums, used, path, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void backtrack(int[] nums, boolean[] used, List<Integer> path, List<List<Integer>> res) {
|
||||||
|
// 当path中元素个数等于nums数组的长度时,说明已构造出一个排列
|
||||||
|
if (path.size() == nums.length) {
|
||||||
|
res.add(new ArrayList<>(path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 遍历数组中的每个数字
|
||||||
|
for (int i = 0; i < nums.length; i++) {
|
||||||
|
// 如果该数字已经在当前排列中使用过,则跳过
|
||||||
|
if (used[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 选择数字nums[i]
|
||||||
|
used[i] = true;
|
||||||
|
path.add(nums[i]);
|
||||||
|
// 递归构造剩余的排列
|
||||||
|
backtrack(nums, used, path, res);
|
||||||
|
// 回溯:撤销选择,尝试其他数字
|
||||||
|
path.remove(path.size() - 1);
|
||||||
|
used[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
src/main/java/backtrack/PermuteUnique.java
Normal file
51
src/main/java/backtrack/PermuteUnique.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package backtrack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目: 47. 全排列 II (permuteUnique)
|
||||||
|
* 描述:给定一个可包含重复数字的序列 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]]
|
||||||
|
|
||||||
|
* 链接:https://leetcode.cn/problems/permutations-ii/
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class PermuteUnique {
|
||||||
|
void backtrack(List<List<Integer>>res,List<Integer>path,int[]nums,int[] visited){
|
||||||
|
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] && visited[i - 1] == 0)
|
||||||
|
continue;
|
||||||
|
if(visited[i]==0){
|
||||||
|
visited[i]=1;
|
||||||
|
path.add(nums[i]);
|
||||||
|
backtrack(res,path,nums,visited);
|
||||||
|
path.remove(path.size()-1);
|
||||||
|
visited[i]=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public List<List<Integer>> permuteUnique(int[] nums) {
|
||||||
|
List<List<Integer>>res=new ArrayList<>();
|
||||||
|
List<Integer>path=new ArrayList<>();
|
||||||
|
int[] visited=new int[nums.length];
|
||||||
|
Arrays.sort(nums);
|
||||||
|
backtrack(res,path,nums,visited);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
43
src/main/java/backtrack/Subsets.java
Normal file
43
src/main/java/backtrack/Subsets.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package backtrack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目: 78. 子集 (subsets)
|
||||||
|
* 描述:给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
|
||||||
|
* 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
|
||||||
|
*
|
||||||
|
示例 1:
|
||||||
|
输入:nums = [1,2,3]
|
||||||
|
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
|
||||||
|
|
||||||
|
示例 2:
|
||||||
|
输入:nums = [0]
|
||||||
|
输出:[[],[0]]
|
||||||
|
|
||||||
|
* 链接:https://leetcode.cn/problems/subsets/
|
||||||
|
*/
|
||||||
|
public class Subsets {
|
||||||
|
public List<List<Integer>> subsets(int[] nums) {
|
||||||
|
List<List<Integer>> res = new ArrayList<>();
|
||||||
|
List<Integer> path = new ArrayList<>();
|
||||||
|
// 从索引0开始递归
|
||||||
|
backtrack(nums, 0, path, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void backtrack(int[] nums, int start, List<Integer> path, List<List<Integer>> res) {
|
||||||
|
// 每个递归节点的当前path都构成了一个子集,将其复制加入结果集合中
|
||||||
|
res.add(new ArrayList<>(path));
|
||||||
|
// 从start开始遍历所有可能的元素
|
||||||
|
for (int i = start; i < nums.length; i++) {
|
||||||
|
// 做选择:将当前数字加入path
|
||||||
|
path.add(nums[i]);
|
||||||
|
// 递归:从下一个位置继续选取,避免重复选择
|
||||||
|
backtrack(nums, i + 1, path, res);
|
||||||
|
// 回溯:撤销选择,继续尝试下一个数字
|
||||||
|
path.remove(path.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
src/main/java/backtrack/SubsetsWithDup.java
Normal file
51
src/main/java/backtrack/SubsetsWithDup.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package backtrack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目: 90. 子集 II (subsetsWithDup)
|
||||||
|
* 描述:给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的 子集(幂集)。
|
||||||
|
* 解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
|
||||||
|
|
||||||
|
示例 1:
|
||||||
|
输入:nums = [1,2,2]
|
||||||
|
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
|
||||||
|
|
||||||
|
* 链接:https://leetcode.cn/problems/subsets-ii/
|
||||||
|
*/
|
||||||
|
//不会,有点难
|
||||||
|
public class SubsetsWithDup {
|
||||||
|
public List<List<Integer>> subsetsWithDup(int[] nums) {
|
||||||
|
List<List<Integer>> res = new ArrayList<>();
|
||||||
|
List<Integer> path = new ArrayList<>();
|
||||||
|
// 排序数组,使重复元素相邻,便于剪枝
|
||||||
|
Arrays.sort(nums);
|
||||||
|
backtrack(res, path, nums, 0);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void backtrack(List<List<Integer>> res, List<Integer> path, int[] nums, int start) {
|
||||||
|
// 每次递归都将当前路径加入结果,注意:空集也会被加入
|
||||||
|
res.add(new ArrayList<>(path));
|
||||||
|
for (int i = start; i < nums.length; i++) {
|
||||||
|
// 如果当前数字和前一个数字相同,并且前一个数字没有被选取(在本层中出现过),则跳过
|
||||||
|
if (i > start && nums[i] == nums[i - 1]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
path.add(nums[i]);
|
||||||
|
backtrack(res, path, nums, i + 1);
|
||||||
|
path.remove(path.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试方法
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SubsetsWithDup solution = new SubsetsWithDup();
|
||||||
|
int[] nums = {1, 2, 2};
|
||||||
|
List<List<Integer>> subsets = solution.subsetsWithDup(nums);
|
||||||
|
System.out.println(subsets);
|
||||||
|
// 输出:[[], [1], [1, 2], [1, 2, 2], [2], [2, 2]]
|
||||||
|
}
|
||||||
|
}
|
41
src/main/java/graph/FloodFill.java
Normal file
41
src/main/java/graph/FloodFill.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package graph;
|
||||||
|
/**
|
||||||
|
* 题目: 733. 图像渲染 (floodFill)
|
||||||
|
* 描述:有一幅以 m x n 的二维整数数组表示的图画 image ,其中 image[i][j] 表示该图画的像素值大小。你也被给予三个整数 sr , sc 和 color 。你应该从像素 image[sr][sc] 开始对图像进行上色 填充 。
|
||||||
|
|
||||||
|
* 为了完成 上色工作:
|
||||||
|
* 从初始像素开始,将其颜色改为 color。
|
||||||
|
* 对初始坐标的 上下左右四个方向上 相邻且与初始像素的原始颜色同色的像素点执行相同操作。
|
||||||
|
* 通过检查与初始像素的原始颜色相同的相邻像素并修改其颜色来继续 重复 此过程。
|
||||||
|
* 当 没有 其它原始颜色的相邻像素时 停止 操作。
|
||||||
|
* 最后返回经过上色渲染 修改 后的图像 。
|
||||||
|
|
||||||
|
*
|
||||||
|
* 示例 1:
|
||||||
|
输入:image = [[1,1,1],[1,1,0],[1,0,1]],sr = 1, sc = 1, color = 2
|
||||||
|
输出:[[2,2,2],[2,2,0],[2,0,1]]
|
||||||
|
|
||||||
|
* 链接:https://leetcode.cn/problems/flood-fill/
|
||||||
|
*/
|
||||||
|
public class FloodFill {
|
||||||
|
void dfs(int[][] image,int[][] visited,int origin,int i,int j,int color){
|
||||||
|
int r=image.length;
|
||||||
|
int c=image[0].length;
|
||||||
|
if(i<0||i>=r||j<0||j>=c||visited[i][j]==1||image[i][j]!=origin)
|
||||||
|
return;
|
||||||
|
image[i][j]=color;
|
||||||
|
visited[i][j]=1;
|
||||||
|
dfs(image,visited,origin,i-1,j,color);
|
||||||
|
dfs(image,visited,origin,i+1,j,color);
|
||||||
|
dfs(image,visited,origin,i,j-1,color);
|
||||||
|
dfs(image,visited,origin,i,j+1,color);
|
||||||
|
}
|
||||||
|
public int[][] floodFill(int[][] image, int sr, int sc, int color) {
|
||||||
|
int r=image.length;
|
||||||
|
int c=image[0].length;
|
||||||
|
int origin=image[sr][sc];
|
||||||
|
int[][] visited=new int[r][c];
|
||||||
|
dfs(image,visited,origin,sr,sc,color);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
}
|
116
src/main/java/graph/Solve.java
Normal file
116
src/main/java/graph/Solve.java
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package graph;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目: 130. 被围绕的区域 (solve)
|
||||||
|
* 描述:给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' 组成,捕获 所有 被围绕的区域:
|
||||||
|
* 连接:一个单元格与水平或垂直方向上相邻的单元格连接。
|
||||||
|
* 区域:连接所有 'O' 的单元格来形成一个区域。
|
||||||
|
* 围绕:如果您可以用 'X' 单元格 连接这个区域,并且区域中没有任何单元格位于 board 边缘,则该区域被 'X' 单元格围绕。
|
||||||
|
* 通过 原地 将输入矩阵中的所有 'O' 替换为 'X' 来 捕获被围绕的区域。你不需要返回任何值。
|
||||||
|
* <p>
|
||||||
|
* 示例 1:
|
||||||
|
* 输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
|
||||||
|
* 输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
|
||||||
|
* <p>
|
||||||
|
* 链接:https://leetcode.cn/problems/surrounded-regions/
|
||||||
|
*/
|
||||||
|
//做出来了,但是很繁琐1
|
||||||
|
public class Solve {
|
||||||
|
// void dfs(char[][] board, Queue<int[]> queue, int i, int j) {
|
||||||
|
// if (!judgeValid(board, i, j) || board[i][j] == 'X')
|
||||||
|
// return;
|
||||||
|
// queue.offer(new int[]{i, j});
|
||||||
|
// board[i][j] = 'X';
|
||||||
|
// dfs(board, queue, i - 1, j);
|
||||||
|
// dfs(board, queue, i + 1, j);
|
||||||
|
// dfs(board, queue, i, j - 1);
|
||||||
|
// dfs(board, queue, i, j + 1);
|
||||||
|
//// board[i][j]='0';
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void solve(char[][] board) {
|
||||||
|
// Queue<int[]> queue = new LinkedList<>();
|
||||||
|
// List<Queue<int[]>> list = new ArrayList<>();
|
||||||
|
// for (int i = 0; i < board.length; i++) {
|
||||||
|
// for (int j = 0; j < board[0].length; j++) {
|
||||||
|
// if (board[i][j] == 'O') {
|
||||||
|
// dfs(board, queue, i, j);
|
||||||
|
// list.add(new LinkedList<>(queue));
|
||||||
|
// queue.clear();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// for (Queue<int[]> queue1 : list) {
|
||||||
|
// int size = queue1.size();
|
||||||
|
// boolean flag = false;
|
||||||
|
// Queue<int[]> temp = new LinkedList<>(queue1);
|
||||||
|
// for (int i = 0; i < size; i++) {
|
||||||
|
// int[] tp = queue1.poll();
|
||||||
|
// if (!judgeValid(board, tp[0]-1, tp[1])||!judgeValid(board, tp[0]+1, tp[1])||!judgeValid(board, tp[0], tp[1]-1)||!judgeValid(board, tp[0], tp[1]+1)) {
|
||||||
|
// flag = true;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (flag) {
|
||||||
|
// while (!temp.isEmpty()) {
|
||||||
|
// int[] tp = temp.poll();
|
||||||
|
// board[tp[0]][tp[1]] = 'O';
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// temp.clear();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// boolean judgeValid(char[][] board, int i, int j) {
|
||||||
|
// int r = board.length;
|
||||||
|
// int c = board[0].length;
|
||||||
|
// return i >= 0 && i < r && j >= 0 && j < c;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
public void solve(char[][] board) {
|
||||||
|
if (board == null || board.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int rows = board.length, cols = board[0].length;
|
||||||
|
// 从边界开始,将所有与边界相连的 'O' 标记为 '#'
|
||||||
|
for (int i = 0; i < rows; i++) {
|
||||||
|
dfs(board, i, 0);
|
||||||
|
dfs(board, i, cols - 1);
|
||||||
|
}
|
||||||
|
for (int j = 0; j < cols; j++) {
|
||||||
|
dfs(board, 0, j);
|
||||||
|
dfs(board, rows - 1, j);
|
||||||
|
}
|
||||||
|
// 遍历整个矩阵,翻转剩余的 'O' 为 'X',同时将标记 '#' 恢复为 'O'
|
||||||
|
for (int i = 0; i < rows; i++) {
|
||||||
|
for (int j = 0; j < cols; j++) {
|
||||||
|
if (board[i][j] == 'O') {
|
||||||
|
board[i][j] = 'X';
|
||||||
|
} else if (board[i][j] == '#') {
|
||||||
|
board[i][j] = 'O';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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] = '#';
|
||||||
|
dfs(board, i - 1, j);
|
||||||
|
dfs(board, i + 1, j);
|
||||||
|
dfs(board, i, j - 1);
|
||||||
|
dfs(board, i, j + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
18
src/test/java/backtrack/SubsetsWithDupTest.java
Normal file
18
src/test/java/backtrack/SubsetsWithDupTest.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package backtrack;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class SubsetsWithDupTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void subsetsWithDup() {
|
||||||
|
int[]nums = {1,2,2};
|
||||||
|
SubsetsWithDup solution = new SubsetsWithDup();
|
||||||
|
List<List<Integer>>res=solution.subsetsWithDup(nums);
|
||||||
|
System.out.println(res);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user