DFS + 剪枝 解决 全排列系列问题
文章目录
- [DFS + 剪枝 解决 全排列系列问题](#DFS + 剪枝 解决 全排列系列问题)
- 一、46.全排列
- [二、47. 全排列 II](#二、47. 全排列 II)
- [三、60. 排列序列](#三、60. 排列序列)
- [四、31. 下一个排列](#四、31. 下一个排列)
- 总结
一、46.全排列

代码如下(示例):
c
class Solution
{
vector<vector<int>> ret;
vector<int> path;
bool check[7];
public:
vector<vector<int>> permute(vector<int>& nums)
{
dfs(nums);
return ret;
}
void dfs(vector<int>& nums)
{
if (path.size() == nums.size())
{
ret.push_back(path);
return;
}
for (int i = 0; i < nums.size(); i++)
{
if (!check[i])
{
path.push_back(nums[i]);
check[i] = true;
dfs(nums);
// 回溯 -> 恢复现场
path.pop_back();
check[i] = false;
}
}
}
};
二、47. 全排列 II

代码如下(示例):
c
class Solution {
vector<int> path;
vector<vector<int>> ret;
bool check[9];
// bfs is so easy
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
dfs(nums, 0);
return ret;
}
void dfs(vector<int>& nums, int pos)
{
if (pos == nums.size()) {
ret.push_back(path);
return;
}
for (int i = 0; i < nums.size(); i++)
{
// 剪枝
if (check[i] == false &&
(i == 0 || nums[i] != nums[i - 1] || check[i - 1] != false)) {
path.push_back(nums[i]);
check[i] = true;
dfs(nums, pos + 1);
path.pop_back(); // 恢复现场
check[i] = false;
}
}
}
};
三、60. 排列序列

在全排列的基础上面加上计数功能即可!
代码如下(示例):
c
class Solution
{
vector<int> path; // 存储当前排列路径
string res; // 最终结果:第k个排列的字符串
bool check[9]; // 标记数字是否被使用(数字范围1~9)
int count = 0; // 计数:当前生成了第几个排列
public:
string getPermutation(int n, int k)
{
memset(check, false, sizeof(check)); // 初始化标记数组
vector<int> nums;
for (int i = 1; i <= n; i++)
{
nums.push_back(i); // 构造 1~n 的数字数组
}
dfs(nums, k);
return res;
}
void dfs(vector<int>& nums, int k)
{
// 剪枝:找到结果后直接返回,不再递归
if (!res.empty())
{
return;
}
// 递归终止条件:路径长度等于数字个数,生成一个完整排列
if (path.size() == nums.size())
{
count++; // 排列计数+1
// 找到第k个排列,转为字符串存储
if (count == k)
{
for (int num : path)
{
res += to_string(num);
}
}
return;
}
for (int i = 0; i < nums.size(); i++)
{
// 只选未使用的数字(本题无重复,无需去重剪枝)
if (!check[i])
{
check[i] = true;
path.push_back(nums[i]);
dfs(nums, k);
path.pop_back(); // 回溯:恢复路径
check[i] = false; // 回溯:恢复标记
}
}
}
};
四、31. 下一个排列

这道题有录音!

代码如下(示例):
c
class Solution {
public:
void nextPermutation(vector<int>& nums)
{
int n = nums.size();
// 步骤1:从后往前找第一个升序对 nums[i] < nums[i+1]
int i = n - 2;
while (i >= 0 && nums[i] >= nums[i + 1])
{
i--;
}
// 步骤2:从后往前找第一个比 nums[i] 大的数 nums[j]
if (i >= 0)
{
int j = n - 1;
while (nums[j] <= nums[i])
{
j--;
}
swap(nums[i], nums[j]); // 交换,增大排列
}
// 步骤3:反转 i+1 到末尾,得到最小升序后缀
reverse(nums.begin() + i + 1, nums.end());
}
};
总结
这篇文章是作者搜集大量面经和资料这里出来的。感谢你的支持
作者wkm是一名中国矿业大学(北京) 大一的新生,希望得到你的关注
如果可以的话,记得一键三联!