内容介绍
给定一个不含重复数字的数组
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 <= nums.length <= 6
-10 <= nums[i] <= 10
nums
中的所有整数 互不相同
完整代码
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
List<Integer> output = new ArrayList<Integer>();
for (int num : nums) {
output.add(num);
}
int n = nums.length;
backtrack(n, output, res, 0);
return res;
}
public void backtrack(int n, List<Integer> output, List<List<Integer>> res, int first) {
// 所有数都填完了
if (first == n) {
res.add(new ArrayList<Integer>(output));
}
for (int i = first; i < n; i++) {
// 动态维护数组
Collections.swap(output, first, i);
// 继续递归填下一个数
backtrack(n, output, res, first + 1);
// 撤销操作
Collections.swap(output, first, i);
}
}
}
思路详解
思路详解:
-
算法设计:
- 回溯法是一种递归算法,通过尝试所有可能的解决方案,并在必要时回溯到之前的步骤来找到所有可能的解。
- 在本例中,回溯法用于生成数组
nums
的所有排列组合。
-
函数定义:
permute
函数是算法的主入口点,它负责创建一个结果列表res
来存储所有可能的排列组合,并初始化一个输出列表output
来存储当前的排列。backtrack
函数是递归函数,用于生成排列组合。它接受当前排列的长度n
、当前排列output
、结果列表res
和当前处理的起始位置first
。
-
初始化:
- 在
permute
函数中,首先创建一个空的结果列表res
和一个初始化的输出列表output
。 - 将
nums
数组中的每个元素添加到output
中。
- 在
-
回溯算法:
backtrack
函数的核心是一个if
语句,它检查是否所有数都已经被填入output
。如果是,说明当前的排列是一个有效的排列,将其添加到结果列表res
中。- 接着,使用一个
for
循环遍历output
中的每个元素,从当前处理的起始位置first
开始。 - 对于每个元素,执行以下操作:
- 使用
Collections.swap
方法将当前元素与first
位置的元素交换,以生成一个新的排列。 - 递归调用
backtrack
函数,处理下一个位置。 - 递归调用结束后,使用
Collections.swap
方法撤销之前的交换操作,以恢复原始排列。
- 使用
-
撤销操作:
- 在递归调用
backtrack
函数之前,使用Collections.swap
方法将当前元素与first
位置的元素交换。 - 在递归调用
backtrack
函数之后,使用Collections.swap
方法撤销之前的交换操作,以恢复原始排列。
- 在递归调用
-
返回结果:
- 遍历
output
中的每个元素并执行上述操作后,返回结果列表res
。
- 遍历
这个算法的时间复杂度为 O(n!),其中 n 是数组 nums
的长度。这是因为算法需要生成所有 n! 个排列组合,每个排列组合都需要 O(n) 的时间来生成。空间复杂度为 O(n),因为需要一个长度为 n 的输出列表 output
来存储中间结果。
知识点精炼
-
回溯法:
- 回溯法是一种递归算法,通过尝试所有可能的解决方案,并在必要时回溯到之前的步骤来找到所有可能的解。
- 在本例中,回溯法用于生成数组
nums
的所有排列组合。
-
递归函数:
- 递归函数是一种函数,它调用自身来解决问题。
- 在本例中,
backtrack
函数是递归函数,用于生成排列组合。
-
列表操作:
- 使用
ArrayList
类来存储数组nums
中的元素。 - 使用
add
方法将元素添加到列表中。 - 使用
swap
方法交换列表中的元素。
- 使用
-
集合操作:
- 使用
Collections
类来提供集合操作的静态方法。 - 在本例中,使用
Collections.swap
方法交换列表中的元素。
- 使用
-
函数定义:
permute
函数是算法的主入口点,它负责创建一个结果列表res
来存储所有可能的排列组合,并初始化一个输出列表output
来存储当前的排列。backtrack
函数是递归函数,用于生成排列组合。它接受当前排列的长度n
、当前排列output
、结果列表res
和当前处理的起始位置first
。
-
变量初始化:
- 在
permute
函数中,首先创建一个空的结果列表res
和一个初始化的输出列表output
。 - 将
nums
数组中的每个元素添加到output
中。
- 在
-
循环结构:
- 使用
for
循环遍历output
中的每个元素,从当前处理的起始位置first
开始。
- 使用
-
逻辑判断:
- 使用条件语句
if
来判断是否所有数都已经被填入output
。
- 使用条件语句
-
返回结果:
- 遍历
output
中的每个元素并执行上述操作后,返回结果列表res
。
- 遍历
题型扩展
题目描述
给定一个字符串 s
,请实现一个函数 countSubstrings
,该函数返回字符串 s
中所有子串的出现次数。
子串是指字符串中任意连续的若干字符组成的序列。例如,对于字符串 "abc"
,其子串包括 "a"
, "b"
, "c"
, "ab"
, "bc"
, "abc"
。
输入输出格式
输入:一个字符串 s
。
输出:一个整数数组,表示 s
中所有子串的出现次数。
示例
输入:"abc"
输出:[1, 1, 1, 2, 2, 3]
解释:子串 "a"
出现 1 次,子串 "b"
出现 1 次,子串 "c"
出现 1 次,子串 "ab"
出现 2 次,子串 "bc"
出现 2 次,子串 "abc"
出现 3 次。
这个题目可以扩展为更复杂的问题,例如:
-
给定一个字符串
s
和一个整数k
,请实现一个函数countSubstringsWithK
,该函数返回字符串s
中所有长度为k
的子串的出现次数。 -
给定一个字符串
s
和一个字符c
,请实现一个函数countSubstringsWithChar
,该函数返回字符串s
中所有包含字符c
的子串的出现次数。 -
给定一个字符串
s
和两个整数m
和n
,请实现一个函数countSubstringsWithRange
,该函数返回字符串s
中所有长度在m
到n
之间的子串的出现次数。
这些问题都可以通过扩展原有的 countSubstrings
函数来实现。例如,对于 countSubstringsWithK
问题,可以在 countSubstrings
函数的基础上添加一个条件来判断子串的长度是否等于 k
。对于 countSubstringsWithChar
问题,可以在 countSubstrings
函数的基础上添加一个条件来判断子串中是否包含字符 c
。对于 countSubstringsWithRange
问题,可以在 countSubstrings
函数的基础上添加两个条件来判断子串的长度是否在 m
到 n
之间。