题目:
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
回溯法:一种通过探索所有可能的候选解来找出所有的解的算法。
public class MySort {
public static void sort() {
int[] nums = {1, 2, 3, 4};
int length = nums.length;
//用于存储得到的结果
List<int[]> result = new ArrayList<>();
//用于标识每个元素的状态,并且进行回溯
boolean[] flag = new boolean[length];
//初始化一个数组
int[] num = new int[length];
doIt(length, nums, 0, num, result, flag);
//遍历结果,打印日志
for (int i = 0; i < result.size(); i++) {
System.err.println(Arrays.toString(result.get(i)));
}
}
public static void doIt(int length, int[] nums, int start, int[] num, List<int[]> list, boolean[] flag) {
//每次给num数组赋值之后都会将start加1,所以当start==length时满足要求
if (start == length) {
//复制数组,并且将数组加到集合里面,如果直接用num,那么得到的结果,每个数组里的值都是一样的
list.add(num.clone());
return;
}
for (int i = 0; i < length; i++) {
//因为每个元素只能被用一次,当状态为true时,说明被用过了
if (flag[i] == true) {
continue;
}
//如果状态不是true就把值赋值给数组,我这里只用了唯一的一个数组去存储,所以,数组里面 的值都是赋值替换的,比较难理解。
num[start] = nums[i];
//将该元素状态改为true,表明该元素已经被用了,在这组结果集中不可以再用了
flag[i] = true;
doIt(length, nums, start + 1, num, list, flag);
//将状态改为false,说明我们已经得到了一组解或者前一个步骤得整个方法走完了,表明该元素可以用了,会继续寻找所有满足结果的解
flag[i] = false;
}
}
}
个人总结用回溯算法的小技巧(可以不看哈,这是自己的一点总结,最重要的还是得搞懂它得原理,能理清逻辑,逻辑里不过来就debug,或者在纸上画一画):
1.首先我们不管三七二十一,就先把这个数组得长度计算出来int length = nums.length;,别问,写下来,肯定要用,算法 题很少不用计算长度的,哈哈
2,看例子,输出,那肯定是个集合,直接写个List<int[]> result = new ArrayList<>();肯定用来存储结果,有了集合那肯定少不了,int[] num = new int[length],都写出来,写着写着,就用到了;
3,回溯么,肯定是有一个存储元素得状态,是个数组,那我们就少不了:boolean[] flag = new boolean[length],每个元素状态初始值都是false;
4,接下来,就单独写个方法,这是重点。哈哈,我们就把那些定义的东西,当方法的参数传进来。
5,回溯么,那我们肯定是需要结束得标识,那肯定是达到了数组得长度。
6,接着就是遍历这个数组,然后把已经用了的元素,把它状态改为true,
7,再递归调用这个方法,最后再把状态改为false,
8,再不断修修改改,debug,你突然发现,答案出来了,是不是很神奇