力扣:回溯算法
组合I
class Solution {
List<List<Integer>> result = new ArrayList(); // 所有结果集
List<Integer> list = new ArrayList(); // 当前结果集
public List<List<Integer>> combine(int n, int k) {
dfs(n, k, 1);
return result;
}
public void dfs(int n, int k, int index) {
if (list.size() == k) { // 当前结果集等于要收集的数量即可存入最终结果集
List<Integer> tem = new ArrayList(list);
result.add(tem);
return;
}
for (int i = index; i <= n; i++) {
list.add(i); // 元素加入当前结果集
dfs(n, k, i + 1); // 递归
list.remove(list.size() - 1); // 该元素组合完成可以移除(回溯)
}
}
}
组合II
class Solution {
List<List<Integer>> result = new ArrayList(); // 所有结果集
Set<List<Integer>> set = new HashSet(); // 结果集去重
List<Integer> list = new ArrayList(); // 当前结果集
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
backTring(candidates, 0, target, 0);
return result;
}
public void backTring(int[] candidates, int sum, int target, int index) {
if (candidates == null || candidates.length < 0) return;
if (sum == target) { // 总和等于目标值是返回当前结果集
List<Integer> tem = new ArrayList(list);
Collections.sort(tem); // 去重(如:1 1 2 和 2 1 1 是一组结果集)
if (!set.contains(tem)) {
result.add(new ArrayList(list));
set.add(tem);
}
return;
}
if (sum > target) { // 当前结果集大于目标值说明当前结果集不对
return;
}
for (int i = index; i < candidates.length; i++) {
sum += candidates[i]; // 当前总和
list.add(candidates[i]); // 当前结果集
backTring(candidates, sum, target, i + 1);
sum -= candidates[i]; // 回溯总和
list.remove(list.size() - 1); // 回溯结果集
}
}
}
组合III
class Solution {
List<List<Integer>> result = new ArrayList(); // 最终结果集
List<Integer> list = new ArrayList(); // 当前结果集
public List<List<Integer>> combinationSum3(int k, int n) {
backCheck(k, n, 1, 0, 0);
return result;
}
public void backCheck(int k, int n, int num, int count, int sum) {
if (count == k && n == sum) { // 元素数量等于k 且 sum等于 n 时为符合的结果集
result.add(new ArrayList(list));
return;
}
if (count > k || n < sum) { // 要求的数量或者总和不对则返回
return;
}
for (int i = num; i <= 9; i++) {
list.add(i);
sum += i;
count++;
backCheck(k, n, i + 1, count, sum);
list.remove(list.size() - 1);
sum -= i;
count--;
}
}
}
分割回文串
class Solution {
List<List<String>> result = new ArrayList(); // 最终结果集
List<String> list = new ArrayList(); // 当前结果集
public String[][] partition(String s) {
int n = s.length();
dfs(0, n, s);
return listToArrays(result); // 集合转换为数组
}
public void dfs(int index, int n, String s) {
if (index == n) { // 指针指向n时说明遍历到字符串末尾,可以返回结果集
result.add(new ArrayList(list));
return;
}
for (int i = index; i < n; i++) {
if (isPalindrome(s.substring(index, i + 1))) { // 如果是回文串则加入当前结果集
list.add(s.substring(index, i + 1)); // 加入结果集
dfs(i + 1, n, s);
list.remove(list.size() - 1); // 回溯
}
}
}
public boolean isPalindrome(String str) { // 判断是否为回文串
int l = 0;
int r = str.length() - 1;
while (l < r) {
if (str.charAt(l) != str.charAt(r)) {
return false;
}
l++;
r--;
}
return true;
}
public String[][] listToArrays (List<List<String>> list) { // 集合转换为数组
int n = list.size();
String[][] arrs = new String[n][];
for (int i = 0; i < n; i++) {
List<String> tem = list.get(i);
String[] arr = tem.toArray(new String[tem.size()]);
arrs[i] = arr;
}
return arrs;
}
}
复原 IP 地址
class Solution {
List<String> res = new ArrayList(); // 所有结果集
List<String> tem = new ArrayList(); // 当前结果集
public List<String> restoreIpAddresses(String s) {
int n = s.length(); // 字符串长度
if (n < 0 || n > 12) return res; // 剪枝
dfs(s, 0, n);
return res;
}
public void dfs(String s, int index, int n) {
if (tem.size() == 4) { // ip地址为四个数字组成,当前结果集等于4即可返回
if (index == n) { // 当前指针指向末尾即可加入最终结果集
StringBuilder sb = new StringBuilder(); // 拼凑成需要的字符串
for (int i = 0; i < 4; i++) {
sb.append(tem.get(i));
if (i != 3) {
sb.append(".");
}
}
res.add(sb.toString()); // 加入到最终结果集
}
return;
}
for (int i = index; i < n && i < index + 3; i++) { // 当前指针
if (isNum(s.substring(index, i + 1))) { //
tem.add(s.substring(index, i + 1));
dfs(s, i + 1, n);
tem.remove(tem.size() - 1);
}
}
}
public boolean isNum(String s) { // 判断是否为合法数字
if (s.length() >= 2 && s.charAt(0) == '0')
return false;
Integer num = Integer.valueOf(s);
if (num > 255)
return false;
return true;
}
}
子集I
class Solution {
List<List<Integer>> res = new ArrayList();
List<Integer> tem = new ArrayList();
public List<List<Integer>> subsets(int[] nums) {
dfs(nums, 0, nums.length);
return res;
}
public void dfs(int[] nums, int index, int n) {
res.add(new ArrayList(tem)); // 每次递归都是一个新子集
for (int i = index; i < n; i++) {
tem.add(nums[i]);
dfs(nums, i + 1, n);
tem.remove(tem.size() - 1);
}
}
}
子集II
class Solution {
List<List<Integer>> res = new ArrayList(); // 所有结果集
List<Integer> list = new ArrayList(); // 当前子集
Set<List<Integer>> set = new HashSet(); // 子集去重
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums); // 排序
dfs(nums, 0, nums.length);
return res;
}
public void dfs(int[] nums, int index, int n) {
res.add(new ArrayList(list)); // 每次递归都是新子集
for (int i = index; i < n; i++) {
if (i != index && nums[i] == nums[i - 1]) continue; // 数组已经排序,如果当前元素等于上一个元素进行递归会有重复子集(如:数组 {1 1} 的子集为 {1} {1 1},如果索引到第二个元素再进行递归则会有重复子集{1}
list.add(nums[i]);
dfs(nums, i + 1, n);
list.remove(list.size() - 1);
}
}
}
最长递增子序列
class Solution {
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length]; // 动态规划+暴力枚举
Arrays.fill(dp, 1); // 每个子序列起始都是1
int max = dp[0];
for (int i = 1; i < nums.length; i++) {
for (int j = 0; j < i; j++) { // 判断当前位置最长子序列是多少
if (nums[j] < nums[i]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
max = max > dp[i] ? max : dp[i];
}
return max;
}
}
全排列I
class Solution {
List<List<Integer>> res = new ArrayList(); // 所有结果集
List<Integer> list = new ArrayList(); // 当前结果集
public List<List<Integer>> permute(int[] nums) {
boolean[] used = new boolean[nums.length]; // 判断当前元素是否加入结果集
dfs(nums, used);
return res;
}
public void dfs(int[] nums, boolean[] used) {
if (list.size() == nums.length) { // 当前结果集长度等于数组长度即可加入所有结果集
res.add(new ArrayList(list));
}
for (int i = 0; i < nums.length; i++) {
if (used[i] == false) { // 判断元素是否加入当前结果集
list.add(nums[i]); // 元素入集合
used[i] = true; // 设置元素状态为被使用
dfs(nums, used);
used[i] = false; // 回溯
list.remove(list.size() - 1);
}
}
}
}
全排列II
class Solution {
List<List<Integer>> res = new ArrayList(); // 所有结果集合
List<Integer> list = new ArrayList(); // 当前结果集合
Set<List<Integer>> set = new HashSet(); // 结果集去重
public List<List<Integer>> permuteUnique(int[] nums) {
boolean[] flag = new boolean[nums.length]; // 判断元素是否加入当前结果集
dfs(nums, flag);
return res;
}
public void dfs(int[] nums, boolean[] flag) {
if (list.size() == nums.length) { // 当前结果集合等于数组长度即可加入最终结果集
List<Integer> tem = new ArrayList(list);
if (!set.contains(tem)) { // 判断该结果集是否在最终结果集中存在
set.add(tem); // 该结果集的顺序存入去重集合中,避免重复加入最终结果集
res.add(tem);
}
}
for (int i = 0; i < nums.length; i++) {
if (flag[i] == false) {
list.add(nums[i]);
flag[i] = true;
dfs(nums, flag);
flag[i] = false;
list.remove(list.size() - 1);
}
}
}
}
N皇后
class Solution {
List<List<String>> res = new ArrayList(); // 最终结果集合
List<String> list = new ArrayList(); //
public List<List<String>> solveNQueens(int n) {
char[][] board = new char[n][n]; // 创建棋盘并初始化
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
board[i][j] = '.';
}
}
dfs(board, n, 0);
return res;
}
public void dfs(char[][] board, int n, int row) {
if (row == n) { // 当第row行也可以放棋子说明该方法合法
res.add(printResult(board, n));
return;
}
for (int col = 0; col < n; col++) {
// 判断该位置是否可以填棋
if (isFlag(board, row, col)) {
board[row][col] = 'Q';
dfs(board, n, row + 1);
board[row][col] = '.';
}
}
}
// 核心方法判断棋子放置位置是否合法
public boolean isFlag(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 < n; i--, j++) { // 右斜线不能有皇后
if (board[i][j] == 'Q') return false;
}
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { // 左斜线不能有皇后
if (board[i][j] == 'Q') return false;
}
return true; // 合法
}
public List<String> printResult(char[][] board, int n) { // 按照题目要求打印结果集
List<String> list = new ArrayList();
for (int i = 0; i < n; i++) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < n; j++) {
sb.append(board[i][j]);
}
list.add(sb.toString());
}
return list;
}
}
解数独
class Solution {
public void solveSudoku(char[][] board) {
dfs(board);
}
public boolean dfs(char[][] board) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] == '.') {
// 填充数字
for (char c = '1'; c <= '9'; c++) {
if (isFlag(board, i, j, c)) {
board[i][j] = c; // 暂时填充该数字
if (dfs(board)) return true;
board[i][j] = '.'; // 递归到后面不合法,回溯
}
}
return false;
}
}
}
return true;
}
// 核心方法,判断该位置的数字是否合法
public boolean isFlag(char[][] board, int row, int col, char c) { // 判断该数字是否重复
for (int i = 0; i < 9; i++) {
// 同行出现过
if (board[i][col] == c) return false;
// 同列出现过
if (board[row][i] == c) return false;
// 九宫格出现过
if (board[(row / 3) * 3 + i / 3][(col / 3) * 3 + i % 3] == c) return false;
}
return true;
}
}