LeetCode 热题 第46题 全排列
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
解题:
#include <vector>
using namespace std;
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> result;
backtrack(nums, 0, result);
return result;
}
private:
void backtrack(vector<int>& nums, int start, vector<vector<int>>& result) {
if (start == nums.size()) {
result.push_back(nums);
return;
}
for (int i = start; i < nums.size(); ++i) {
// 交换当前元素与起始位置元素
swap(nums[start], nums[i]);
// 递归调用,填充下一个数
backtrack(nums, start + 1, result);
// 回溯,撤销选择
swap(nums[start], nums[i]);
}
}
};
关键点
start
和i
的作用:start
表示当前正在处理的数组索引位置,而i
是一个循环变量,它从start
开始遍历到数组的末尾。这意呀着对于每个start
,i
不仅仅等于start
,还会取start
之后的所有值。- 为什么交换?:交换
nums[start]
和nums[i]
的目的是将i
位置上的元素固定到start
位置,然后递归地对剩余部分进行排列。当i == start
时,实际上没有发生实际的交换,因为是同一个位置的元素互换;但这并不影响算法的正确性,因为它确保了每个元素都有机会被放置在start
位置。 - 回溯:每次递归调用后,我们再次交换
nums[start]
和nums[i]
来恢复数组到之前的状态,这样可以尝试其他可能性。这个过程称为回溯。
实现过程
初始状态
nums = [1, 2, 3]
start = 0
第一层递归(start = 0)
第一次调用 (i = 0
)
- 交换:
swap(nums[0], nums[0])
->nums = [1, 2, 3]
(无变化) - 递归调用:
backtrack(nums, 1, result)
第二层递归(start = 1)
第一次调用 (i = 1
)
- 交换:
swap(nums[1], nums[1])
->nums = [1, 2, 3]
(无变化) - 递归调用:
backtrack(nums, 2, result)
第三层递归(start = 2)
- 交换:
swap(nums[2], nums[2])
->nums = [1, 2, 3]
(无变化) - 保存排列:
result.push_back([1, 2, 3])
- 回溯:
swap(nums[2], nums[2])
->nums = [1, 2, 3]
(无变化)
第二次调用 (i = 2
)
- 交换:
swap(nums[1], nums[2])
->nums = [1, 3, 2]
- 递归调用:
backtrack(nums, 2, result)
第三层递归(start = 2)
- 交换:
swap(nums[2], nums[2])
->nums = [1, 3, 2]
(无变化) - 保存排列:
result.push_back([1, 3, 2])
- 回溯:
swap(nums[2], nums[2])
->nums = [1, 3, 2]
(无变化) - 回溯:
swap(nums[1], nums[2])
->nums = [1, 2, 3]
(恢复原状)
第二次调用 (i = 1
)
- 交换:
swap(nums[0], nums[1])
->nums = [2, 1, 3]
- 递归调用:
backtrack(nums, 1, result)
第二层递归(start = 1)
第一次调用 (i = 1
)
- 交换:
swap(nums[1], nums[1])
->nums = [2, 1, 3]
(无变化) - 递归调用:
backtrack(nums, 2, result)
第三层递归(start = 2)
- 交换:
swap(nums[2], nums[2])
->nums = [2, 1, 3]
(无变化) - 保存排列:
result.push_back([2, 1, 3])
- 回溯:
swap(nums[2], nums[2])
->nums = [2, 1, 3]
(无变化)
第二次调用 (i = 2
)
- 交换:
swap(nums[1], nums[2])
->nums = [2, 3, 1]
- 递归调用:
backtrack(nums, 2, result)
第三层递归(start = 2)
- 交换:
swap(nums[2], nums[2])
->nums = [2, 3, 1]
(无变化) - 保存排列:
result.push_back([2, 3, 1])
- 回溯:
swap(nums[2], nums[2])
->nums = [2, 3, 1]
(无变化) - 回溯:
swap(nums[1], nums[2])
->nums = [2, 1, 3]
(恢复原状) - 回溯:
swap(nums[0], nums[1])
->nums = [1, 2, 3]
(恢复原状)
第三次调用 (i = 2
)
- 交换:
swap(nums[0], nums[2])
->nums = [3, 2, 1]
- 递归调用:
backtrack(nums, 1, result)
第二层递归(start = 1)
第一次调用 (i = 1
)
- 交换:
swap(nums[1], nums[1])
->nums = [3, 2, 1]
(无变化) - 递归调用:
backtrack(nums, 2, result)
第三层递归(start = 2)
- 交换:
swap(nums[2], nums[2])
->nums = [3, 2, 1]
(无变化) - 保存排列:
result.push_back([3, 2, 1])
- 回溯:
swap(nums[2], nums[2])
->nums = [3, 2, 1]
(无变化)
第二次调用 (i = 2
)
- 交换:
swap(nums[1], nums[2])
->nums = [3, 1, 2]
- 递归调用:
backtrack(nums, 2, result)
第三层递归(start = 2)
- 交换:
swap(nums[2], nums[2])
->nums = [3, 1, 2]
(无变化) - 保存排列:
result.push_back([3, 1, 2])
- 回溯:
swap(nums[2], nums[2])
->nums = [3, 1, 2]
(无变化) - 回溯:
swap(nums[1], nums[2])
->nums = [3, 2, 1]
(恢复原状) - 回溯:
swap(nums[0], nums[2])
->nums = [1, 2, 3]
(恢复原状)
最终结果
最终的结果将是所有可能的排列组合:
[
[1, 2, 3],
[1, 3, 2],
[2, 1, 3],
[2, 3, 1],
[3, 1, 2],
[3, 2, 1]
]
通过上述步骤,我们可以看到每个元素都有机会被放置在每一个位置上,并且在每次递归返回后,我们都恢复了数组到之前的状态,以确保可以探索所有可能的排列组合。
如果想不明白的话建议在草稿纸上一步一步实现程序实现过程