Logo

全排列问题

即给定一个集合,给出所有的排列情况 解法是回溯算法🐸

无重复元素时

为了实现每个元素只被选择一次,我们不用在每次选择时都检测本次的元素是否在已排列的数字中,我们考虑引入一个布尔型数组 selected ,其中 selected[i] 表示 choices[i] 是否已被选择,并基于它实现以下剪枝操作。

/* 回溯算法:全排列 I */
void backtrack(vector<int> &state, const vector<int> &choices, vector<bool> &selected, vector<vector<int>> &res) {
    // 当状态长度等于元素数量时,记录解
    if (state.size() == choices.size()) {
        res.push_back(state);
        return;
    }
    // 遍历所有选择
    for (int i = 0; i < choices.size(); i++) {
        int choice = choices[i];
        // 剪枝:不允许重复选择元素
        if (!selected[i]) {
            // 尝试:做出选择,更新状态
            selected[i] = true;
            state.push_back(choice);
            // 进行下一轮选择
            backtrack(state, choices, selected, res);
            // 回退:撤销选择,恢复到之前的状态
            selected[i] = false;
            state.pop_back();
        }
    }
}

/* 全排列 I */
vector<vector<int>> permutationsI(vector<int> nums) {
    vector<int> state;
    vector<bool> selected(nums.size(), false);
    vector<vector<int>> res;
    backtrack(state, nums, selected, res);
    return res;
}

有重复元素时

需要进行相等元素剪枝,即在本次遍历可能性时,对于重复的元素,只遍历一次 剪枝 在上一题的代码的基础上,我们考虑在每一轮选择中开启一个哈希集合 duplicated ,用于记录该轮中已经尝试过的元素,并将重复元素剪枝

/* 回溯算法:全排列 II */
void backtrack(vector<int> &state, const vector<int> &choices, vector<bool> &selected, vector<vector<int>> &res) {
    // 当状态长度等于元素数量时,记录解
    if (state.size() == choices.size()) {
        res.push_back(state);
        return;
    }
    // 遍历所有选择
    unordered_set<int> duplicated;
    for (int i = 0; i < choices.size(); i++) {
        int choice = choices[i];
        // 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
        if (!selected[i] && duplicated.find(choice) == duplicated.end()) {
            // 尝试:做出选择,更新状态
            duplicated.emplace(choice); // 记录选择过的元素值
            selected[i] = true;
            state.push_back(choice);
            // 进行下一轮选择
            backtrack(state, choices, selected, res);
            // 回退:撤销选择,恢复到之前的状态
            selected[i] = false;
            state.pop_back();
        }
    }
}

/* 全排列 II */
vector<vector<int>> permutationsII(vector<int> nums) {
    vector<int> state;
    vector<bool> selected(nums.size(), false);
    vector<vector<int>> res;
    backtrack(state, nums, selected, res);
    return res;
}

效率分析

假设元素两两之间互不相同,则 n 个元素共有 n! 种排列(阶乘);在记录结果时,需要复制长度为 n 的列表,使用O(n)O(n) 时间。因此时间复杂度为 O(n!n)O(n!n) 。

最大递归深度为 n ,使用 O(n) 栈帧空间。selected 使用 O(n)O(n) 空间。同一时刻最多共有 n 个 duplicated ,使用 O(n2)O(n^2) 空间。因此空间复杂度为 O(n2)O(n^2)

© 2025 All rights reservedBuilt with Flowershow Cloud

Built with LogoFlowershow Cloud