题目来源:. - 力扣(LeetCode)
题目思路分析
题目要求生成一个数组的所有唯一排列。这意味着对于输入数组中的重复元素,它们在同一排列中的相对位置改变形成的排列仍然被视为同一种排列,不应重复计入结果中。为了解决这个问题,我们可以使用回溯算法,并在递归过程中通过一些条件跳过重复的情况。
-
排序:首先,对输入数组进行排序。这是为了确保在回溯过程中,当我们遇到相同的元素时,它们总是按照相同的顺序被处理。这有助于我们检测并跳过重复的排列。
-
回溯:使用回溯算法生成所有可能的排列。在每一步中,我们尝试将每个未使用的元素添加到当前排列的末尾,并递归地继续处理剩余的元素。
-
跳过重复:为了避免生成重复的排列,我们在回溯过程中添加了一个检查。如果当前元素与前一个元素相同,并且前一个元素在当前排列中未被使用(即它还在等待被添加到排列中),则我们跳过当前元素。这是因为如果我们将当前元素添加到排列中,它会生成一个与先前将前一个相同元素添加到排列中相同的排列。
代码:
#include <vector>
#include <algorithm>
class Solution {
public:
// 回溯函数,用于生成排列
void Backstracking(vector<vector<int>>& ans, vector<int>& pos,
vector<int>& nums, vector<bool>& set) {
// 当当前排列的长度等于输入数组的长度时,将其添加到结果中
if (pos.size() == nums.size()) {
ans.push_back(pos);
return;
}
// 遍历输入数组的每个元素
for (int i = 0; i < nums.size(); i++) {
// 如果当前元素与前一个元素相同,并且前一个元素在当前排列中未被使用,则跳过
if (i > 0 && nums[i] == nums[i - 1] && set[i - 1] == false) {
continue;
}
// 如果当前元素还未被使用
if (set[i] == false) {
// 标记当前元素为已使用
set[i] = true;
// 将当前元素添加到当前排列中
pos.push_back(nums[i]);
// 递归调用回溯函数,继续处理剩余的元素
Backstracking(ans, pos, nums, set);
// 回溯:撤销选择,将当前元素从当前排列中移除,并标记为未使用
pos.pop_back();
set[i] = false;
}
}
}
// 主函数,用于生成所有唯一排列
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>> ans; // 存储所有唯一排列的结果
vector<int> pos; // 当前排列
vector<bool> set(nums.size(), false); // 标记数组,用于记录哪些元素已被使用
// 对输入数组进行排序
sort(nums.begin(), nums.end());
// 调用回溯函数开始生成排列
Backstracking(ans, pos, nums, set);
return ans; // 返回结果
}
};
知识点摘要
-
回溯算法:一种通过递归尝试所有可能的选择来解决问题的算法。在每一步中,算法做出一个选择,并递归地继续处理剩余的问题。如果当前选择导致无法找到解决方案,算法会“回溯”到上一步,并尝试另一种选择。
-
排序:对数组进行排序,以便在回溯过程中能够更容易地检测并跳过重复的排列。
-
条件判断:在回溯过程中,使用条件判断来跳过那些会导致生成重复排列的选择。
通过排序和回溯算法,我们可以有效地生成一个数组的所有唯一排列。排序确保了相同元素在数组中的相对位置是固定的,这有助于我们在回溯过程中检测并跳过重复的排列。通过递归地尝试每个元素,并在每一步中做出选择,我们可以生成所有可能的排列。最后,通过条件判断跳过那些会导致重复的选择,我们确保了结果的唯一性。这种方法不仅解决了问题,而且通过排序和条件判断的优化,提高了算法的效率。
1801



被折叠的 条评论
为什么被折叠?



