11.全排列
- 题目描述
给定一个不含重复数字的数组 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]]
- 题目分析
本题时是求排列数,即[1,2,3]和[1,3,2]被视为两个不同的集合,这与组合数不同的是组合数应该考虑如何避免重复,我们用到了startIndex去标记每次遍历的数来实现,那么对于排列数,我们应该考虑的是,怎么可以不漏掉每一个排列数呢?
通过观察,每个排列数均在叶子节点,在每次递归过程中,我们只需要将还未遍历的元素加入到我们的集合之中,就可以完成这一操作。对于如何知道哪个元素已经遍历,哪个元素为遍历,我们可以使用一个used数组去标记,如果使用过标记为1.这样我们每次把used元素中为0的元素加入到集合之中,便可以实现此方法。
1.首先,定义了两个全局变量 path 和 result,分别用于存储当前的路径和最终的结果。path 是一个链表,用于暂时存储当前的排列,result 是一个列表,用于存储所有的排列组合。
2.然后,定义了一个 permute 方法,接受一个整数数组 nums 作为参数,并返回所有排列的列表。在该方法中,首先初始化了一个长度与 nums 相同的数组 used,用于标记每个数字是否被使用过。然后调用 backtrack 方法开始搜索排列。
3.在 backtrack 方法中,首先判断当前的排列长度是否等于 nums 的长度,如果是,则将当前排列加入到 result 中,并返回。接着,使用一个循环遍历 nums 数组中的每个元素,对于每个元素,如果它已经被使用过,则跳过当前循环。如果没有被使用过,则将其标记为已使用,添加到 path 中,然后递归调用 backtrack 方法继续搜索下一层排列,当递归结束后,将当前元素从 path 中移除,并将其标记为未使用,以便尝试其他可能的排列组合。
4.通过这样的回溯过程,最终可以得到所有的排列组合。
- Java代码实现
// 定义一个全局变量,用于存储当前的路径
LinkedList<Integer> path = new LinkedList<>();
// 定义一个全局变量,用于存储最终的结果
List<List<Integer>> result = new ArrayList<>();
/**
* 生成给定数组的所有排列
* @param nums 给定的整数数组
* @return 所有排列的列表
*/
public List<List<Integer>> permute(int[] nums) {
// 初始化一个数组,用于标记每个数字是否被使用过
int[] used = new int[nums.length];
// 开始回溯搜索排列
backtrack(nums, used);
// 返回最终的结果
return result;
}
/**
* 回溯方法,用于生成排列
* @param nums 给定的整数数组
* @param used 数字使用情况的数组
*/
private void backtrack(int[] nums, int[] used) {
// 当当前路径长度等于数组长度时,将当前排列加入到结果中
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
return;
}
// 遍历数组中的每个元素
for (int i = 0; i < nums.length; i++) {
// 如果当前元素已经被使用过,则跳过
if (used[i] == 1) continue;
// 标记当前元素为已使用
used[i] = 1;
// 将当前元素添加到路径中
path.add(nums[i]);
// 递归调用回溯方法,继续生成排列
backtrack(nums, used);
// 将当前元素从路径中移除
path.removeLast();
// 标记当前元素为未使用,以便尝试其他组合
used[i] = 0;
}
}