回溯法
代码方面,回溯算法的框架:
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
[if 其他选择:行为]
做选择
backtrack(路径, 选择列表)
撤销选择
排列问题
问题描述:输入一个不包含重复数字的数组 nums
,返回这些数字的全部排列。
这里根据公式
A
n
m
=
A
n
n
=
n
(
n
−
1
)
A^nm = A ^n n=n(n-1)
Anm=Ann=n(n−1)
可以算出有n(n-1)
种可能。
package BackTrace;
import java.util.ArrayList;
import java.util.List;
/**
* @author junfeng.lin
* @date 2021/3/31 17:07
*/
public class AllRank {
//结果集合
public static List<List<Integer>> lists = new ArrayList<>();
//使用数组来判断元素是否被访问过
public static boolean[] isVisit;
public static void getResult(int[] nums) {
isVisit = new boolean[nums.length];
dfs(nums ,new ArrayList<>());
}
public static void dfs(int[] nums ,List<Integer> cur) {
if (cur.size() == nums.length) {
//此处要新建集合,cur的值会在后续被改变,结果也被改变了
lists.add(new ArrayList<>(cur));
return;
}
//每次都从0开始遍历
for (int i = 0;i < nums.length;i++) {
if (isVisit[i]) continue;
isVisit[i] = true;
cur.add(nums[i]);
dfs(nums ,cur);
cur.remove(cur.size() - 1);
isVisit[i] = false;
}
}
public static void main(String[] args) {
getResult(new int[]{1,2,3});
System.out.println(lists);
}
}
输出:
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
子集问题
问题描述:输入一个不包含重复数字的数组nums
,返回这些数字的所有子集
package BackTrace;
import java.util.ArrayList;
import java.util.List;
/**
* @author junfeng.lin
* @date 2021/3/31 19:54
*/
public class PowerSet {
//结果集合
public static List<List<Integer>> lists;
//使用数组来判断元素是否被访问过
public static boolean[] isVisit;
public static void getPowerSet(int[] nums) {
lists = new ArrayList<>();
isVisit = new boolean[nums.length];
dfs(nums ,new ArrayList<>() ,0);
}
/**
* 回溯法
* @param nums
* @param cur
* @param index
*/
public static void dfs(int[] nums ,List<Integer> cur ,int index) {
//走到了叶子结点,添加到结果中
if (index == nums.length) {
lists.add(new ArrayList<>(cur));
return;
}
for (int i = index;i < nums.length;i++) {
if (isVisit[i]) continue;
//不加入这个数
isVisit[i] = true;
dfs(nums ,cur ,index + 1);
isVisit[i] = false;
//不加入这个数
isVisit[i] = true;
cur.add(nums[i]);
dfs(nums ,cur ,index + 1);
cur.remove(cur.size() - 1);
isVisit[i] = false;
}
}
public static void main(String[] args) {
int[] nums = new int[]{1,2,3};
getPowerSet(nums);
System.out.println(lists);
}
}
输出:
[[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
组合问题
问题描述:从m个球里(编号为1,2,3…,m)一次取n个球,其中m>n,记录取出球的编号,枚举所有的可能性。
假设n为4,k为2,则根据公式
C
n
m
=
C
2
4
=
6
C^nm = C ^2 4=6
Cnm=C24=6
可以算出有6种可能。
package BackTrace;
import java.util.ArrayList;
import java.util.List;
/**
* @author junfeng.lin
* @date 2021/3/31 22:12
*/
public class Combination {
public static List<List<Integer>> lists;
//使用数组来判断元素是否被访问过
public static boolean[] isVisit;
public static void getCombination(int n ,int k) {
lists = new ArrayList<>();
isVisit = new boolean[n + 1];
dfs(n ,k ,new ArrayList<>() ,1);
}
public static void dfs(int n ,int k ,List<Integer> cur ,int start) {
//当集合中有k个元素时,说明出现了一种情况
if (k == cur.size()) {
lists.add(new ArrayList<>(cur));
return;
}
for (int i = start;i <= n;i++) {
cur.add(i);
//注意从i + 1 开始,而不是从start开始向下递归
dfs(n ,k ,cur ,i + 1);
cur.remove(cur.size() - 1);
}
}
public static void main(String[] args) {
getCombination(4 ,2);
System.out.println(lists);
}
}
输出:
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]