题目
给定一个 没有重复 数字的序列,返回其所有可能的全排列,如图:
分析
首先看到这道题,简单的暴力算法很难得出,因为数字的个数未知,一味地将数字不断的打乱重写,有两个问题,第一是很难做到将每种可能都列举出来,另一个是,时间复杂度过高,为阶层式的叠加,一旦给的数字过多,可能无法被计算得出。
看到这道题,按照我们正常的思路,应该会采取定一动一的方法来排列,这种思维方法和回溯算法的想法是一样的,比如我们先定好首个字母,以 [1,2,3] 为例,我们第一次先定好首个数字 1 ,然后求 [2,3] 的全排列,对于 [2,3] 的全排列,又可以采取定好首个字母来确定,以此类推,将首数字为 1 的情况列举完以后,在查看其他的情况。
我们可以用这道题,详细分析一下回溯算法,回溯算法可以有如下模板,时间复杂度为O(n2):
result = []
func backtrack(路径,选择列表) {
if 满足结束条件 {
result.add(路径)
}
return
for 选择 in 选择列表 {
做选择
backtrack(路径,选择列表)
撤销选择
}
}
我们用模板,则有以下算法可以:
var result [][]int
func permute(nums []int) [][]int {
//先设定最后结果,路径和被使用过的数字
result = [][]int{}
pathNums := []int{}
used := make([]bool, len(nums))
backtrack(nums, pathNums, used)
return result
}
func backtrack(nums, pathNums []int, used []bool) {
//设定结束条件,当走完这个数列是,回溯结束
if len(nums) == len(pathNums) {
tmp := make([]int, len(nums))
//只使用公用数据
copy(tmp, pathNums)
result = append(result, tmp)
return
}
for i:=0; i<len(nums); i++ {
//设定判断,若未使用,进入内部并标记此数字被使用
if !used[i] {
used[i] = true
//将数字放入尾部,
pathNums = append(pathNums, nums[i])
//继续搜索
backtrack(nums, pathNums, used)
//撤销一步
pathNums = pathNums[:len(pathNums)-1]
//标记为未使用
used[i] = false
}
}
}