dfs+回溯写题两种思路
主要框架
public void dfs(选择列表){
//1.找到结束条件
//2.遍历所有可能性
//2.1做选择
//2.2 递归调用自己进一步深度遍历
//3.回撤选择
}
- dfs函数的参数变量我觉得是越少越好,所以将一些不怎么改变的变量设置为全局变量更容易理清思路
1.遍历的过程不停的往中间变量添加数据
剑指 Offer 38. 字符串的排列
static Set<String> res;
static char[] ch;
static boolean[] ch_helper;
public static String[] permutation(String s) {
res = new HashSet<>();
ch = s.toCharArray();
ch_helper = new boolean[ch.length];
dfs("");
return res.toArray(new String[res.size()]);
}
private static void dfs(String str) {
//1.结束条件
if(str.length() == ch.length){
res.add(str);
}
//2.遍历所有可能性
for(int i = 0; i < ch.length; i++){
if(ch_helper[i] != true){
ch_helper[i] = true;
dfs(str + ch[i]);
ch_helper[i] = false;
}
}
}
46. 全排列
static List<List<Integer>> res;
static LinkedList<Integer> tmp;
public static List<List<Integer>> permute(int[] nums) {
res = new LinkedList<>();
tmp = new LinkedList<>();
dfs(nums);
return res;
}
private static void dfs(int[] nums) {
if(tmp.size() == nums.length){
res.add(new ArrayList<>(tmp));
return;
}
for(int num : nums){
if(tmp.contains(num)){
continue;
}
tmp.add(num);
dfs(nums);
tmp.removeLast();
}
2. 遍历的过程改变原列表,这种一般会从下标0开始
剑指 Offer 38. 字符串的排列
static List<String> res;
static char[] ch;
public static String[] permutation(String s) {
res = new LinkedList<>();
ch = s.toCharArray();
dfs(0);
return res.toArray(new String[res.size()]);
}
private static void dfs(int i) {
//1.结束条件
if (i == ch.length - 1){
res.add(String.valueOf(ch));
return;
}
//2.选择列表
Set<Character> set = new HashSet<>();
for(int idx = i; idx < ch.length; idx++){
if(set.contains(ch[idx]))
continue;
set.add(ch[idx]);
//2.1选择
swap(ch, idx, i);
//2.2 进一步
dfs(i + 1);
//3.回溯
swap(ch, idx, i);
}
}
private static void swap(char[] ch, int idx, int i) {
char tmp = ch[idx];
ch[idx] = ch[i];
ch[i] = tmp;
}
46. 全排列
- 么有剪枝
static List<String> res;
static char[] ch;
public static String[] permutation(String s) {
res = new LinkedList<>();
ch = s.toCharArray();
dfs(0);
return res.toArray(new String[res.size()]);
}
private static void dfs(int i) {
//1.结束条件
if (i == ch.length - 1){
res.add(String.valueOf(ch));
return;
}
//2.选择列表
for(int idx = i; idx < ch.length; idx++){
set.add(ch[idx]);
//2.1选择
swap(ch, idx, i);
//2.2 进一步
dfs(i + 1);
//3.回溯
swap(ch, idx, i);
}
}
private static void swap(char[] ch, int idx, int i) {
char tmp = ch[idx];
ch[idx] = ch[i];
ch[i] = tmp;
}
- 带剪枝
static List<String> res;
static char[] ch;
public static String[] permutation(String s) {
res = new LinkedList<>();
ch = s.toCharArray();
dfs(0);
return res.toArray(new String[res.size()]);
}
private static void dfs(int i) {
//1.结束条件
if (i == ch.length - 1){
res.add(String.valueOf(ch));
return;
}
//2.选择列表
Set<Character> set = new HashSet<>();
for(int idx = i; idx < ch.length; idx++){
if(set.contains(ch[idx]))
continue;
set.add(ch[idx]);
//2.1选择
swap(ch, idx, i);
//2.2 进一步
dfs(i + 1);
//3.回溯
swap(ch, idx, i);
}
}
private static void swap(char[] ch, int idx, int i) {
char tmp = ch[idx];
ch[idx] = ch[i];
ch[i] = tmp;
}
力扣 51N 皇后
public class solveNQueens {
public static List<List<String>> solveNQueens(int n) {
char[][] queens = new char[n][n]; List<List<String>> res = new LinkedList<>(); backtrack(queens, res,0, n); return res; }
private static void backtrack(char[][] queens, List<List<String>> res, int i, int n) {
//结束,生成结果 if (i == n){
res.add(generateBoard(queens)); return; }
//如果放得下 for(int j = 0; j < n; j++){
if (valid(queens, i, j)){
queens[i][j] = 'Q'; backtrack(queens, res,i + 1, n); //撤销 queens[i][j] = '.'; }
}
}
private static List<String> generateBoard(char[][] queens) {
List<String> path = new ArrayList<>(); for (int i = 0; i < queens.length; i++) {
for(int j = 0; j < queens[0].length; j++)
if (queens[i][j] != 'Q')
queens[i][j] = '.'; path.add(new String(queens[i])); }
return path; }
//row表示第几行,col表示第几列 private static boolean valid(char[][] chess, int row, int col) {
//判断当前列有没有皇后,因为他是一行一行往下走的, //我们只需要检查走过的行数即可,通俗一点就是判断当前 //坐标位置的上面有没有皇后 for (int i = 0; i < row; i++) {
if (chess[i][col] == 'Q') {
return false; }
}
//判断当前坐标的右上角有没有皇后 for (int i = row - 1, j = col + 1; i >= 0 && j < chess.length; i--, j++) {
if (chess[i][j] == 'Q') {
return false; }
}
//判断当前坐标的左上角有没有皇后 for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (chess[i][j] == 'Q') {
return false; }
}
return true; }
}
l力扣 301 删除无效的括号
package leetcode.dfs.hot301_removeInvalidParentheses;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 301. 删除无效的括号
* 给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。
*
* 返回所有可能的结果。答案可以按 任意顺序 返回。
*
* 示例 1:
*
* 输入:s = "()())()"
* 输出:["(())()","()()()"]
* 示例 2:
*
* 输入:s = "(a)())()"
* 输出:["(a())()","(a)()()"]
* 示例 3:
*
* 输入:s = ")("
* 输出:[""]
*/
public class removeInvalidParentheses_2 {
static int len;
static String str;
public static List<String> removeInvalidParentheses(String s) {
len = s.length();
str = s;
Set<String> res = new HashSet<>();
int leftRemove = 0, rightRemove = 0;
for(int i = 0; i < len; i++){
if(s.charAt(i) == '(') leftRemove ++;
else if(s.charAt(i) == ')'){
if(leftRemove == 0){
rightRemove ++;
}else{
leftRemove--;
}
}
}
dfs(0, 0, 0, leftRemove, rightRemove, "", res);
return new ArrayList<>(res);
}
public static void dfs(int idx, int leftCount, int rightCount, int leftRemove, int rightRemove, String tmp, Set<String> res){
// 0. 结束条件
if(idx == len){
if(leftRemove == 0 && rightRemove == 0) {
res.add(tmp);
}
return;
}
// 1. 删除当前字符
if(str.charAt(idx) == '(' && leftRemove > 0){
dfs(idx + 1, leftCount, rightCount, leftRemove - 1, rightRemove,tmp, res);
}
if(str.charAt(idx) == ')' && rightRemove > 0){
dfs(idx + 1, leftCount, rightCount, leftRemove, rightRemove - 1,tmp, res);
}
// 2. 保留当前字符,分三种情况
tmp = tmp+str.charAt(idx);
// 2.1 保留(
if(str.charAt(idx) == '('){
dfs(idx + 1, leftCount+1, rightCount, leftRemove, rightRemove,tmp, res);
}else if(str.charAt(idx) == ')') {
// 保留 )
if (leftCount > rightCount)
dfs(idx + 1, leftCount, rightCount + 1, leftRemove, rightRemove, tmp, res);
// else{
// 剪枝啦,当rightCount >= leftCount时已经无解决
// }
}else{
// 2.3 保留其他字符
dfs(idx + 1, leftCount, rightCount, leftRemove, rightRemove, tmp, res);
}
}
public static void main(String[] args) {
String str = "()())()";
System.out.println(str);
List<String> res = removeInvalidParentheses(str);
System.out.println(res);
}
}
offer 38 字符串的排列
class Solution {
Set<String> res;
boolean[] visited;
char[] chars;
public String[] permutation(String s) {
visited = new boolean[s.length()];
chars = s.toCharArray();
res = new HashSet<>();
dfs("");
return res.toArray(new String[res.size()]);
}
public void dfs(String tmp){
if(tmp.length() == visited.length){
res.add(tmp);
return;
}
for(int i = 0; i < chars.length; i++){
if(visited[i] == false){
visited[i] = true;
dfs(tmp + chars[i]);
visited[i] = false;
}
}
}
}