46 - Permutations(全排列和递归)

本文介绍了一种生成所有可能的全排列的方法。通过递归交换元素位置实现,将大问题拆解为小问题来求解。具体步骤包括:1) 交换首个元素与其他所有元素;2) 固定首个元素后进入下一层递归;3) 直至最后一个元素固定并输出结果。

Given a collection of distinct numbers, return all possible permutations.

For example,
[1,2,3] have the following permutations:
[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1].
Subscribe to see which companies asked this question


思路:

一、全排列

where for the length of n, the permutations can be generated by
(1) Swap the 1st element with all the elements, including itself.
(2) Then the 1st element is fixed, go to the next element.
(3) Until the last element is fixed. Output.
It's more clear in the figure above. The key point is to make the big problem into smaller problem, here is how to convert the length n permutation into length n-1 permutation problem.


代码:

class Solution {
public:
     
    void perm(vector<int> num,int k,int n, vector<vector<int> > &res){
        if (k==n){
            res.push_back(num);
        }else{
            for (int i=k;i<=n;i++){
                int tmp = num[k];
                num[k]=num[i];
                num[i]=tmp;
                 
                perm(num,k+1,n,res);
                 
                tmp = num[k];
                num[k]=num[i];
                num[i]=tmp;
            }
        }
    }
 
    vector<vector<int> > permute(vector<int> &num) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        vector<vector<int> > res;
        perm(num,0,(num.size()-1),res);
        return res;
    }
};



<think> 我们考虑生成0到n-1的全排列全排列是指从n个不同元素中取出n个元素按照一定的顺序排成一列。这里我们要求生成所有可能的排列,且元素为0,1,2,...,n-1。 根据引用[1]的思想,我们可以通过递归的方式,从第一位开始,每一位都尝试0到n-1的数字,但要注意不能重复。实际上,引用[1]的代码是生成n位十进制数的全排列(允许前导0,但打印时去掉前导0),但这里我们要求的是0到n-1的全排列,每个数字恰好出现一次。 因此,我们需要一个标记数组来记录哪些数字已经被使用过,以避免重复。 下面给出一个递归方法(回溯法)来生成全排列: 1. 使用一个数组`perm`来存储当前正在生成的排列。 2. 使用一个布尔数组`used`来记录哪些数字已经被使用过。 3. 递归函数:从索引0开始,对于当前位置,尝试每一个数字(0到n-1),如果这个数字还没有被使用,则将它放在当前位置,并标记为已使用,然后递归处理下一个位置。 4. 当处理到最后一个位置(索引为n-1)时,说明已经生成了一个完整的排列,将其保存或打印。 5. 回溯:在递归返回后,要撤销当前选择的数字,以便尝试下一个数字。 另外,引用[3]中给出了有重复元素的全排列方法,但我们这里没有重复元素,所以可以简化。 具体步骤如下(伪代码): void generate_permutations(int n, int index, int[] perm, bool[] used) 如果 index 等于 n: 打印或保存当前排列perm 返回 否则: 循环数字d从0到n-1: 如果 used[d] 为 false: perm[index] = d used[d] = true generate_permutations(n, index+1, perm, used) used[d] = false // 回溯 注意:初始时,used数组全部为false,perm数组长度为n,index从0开始。 我们也可以使用C++代码来实现。由于问题要求生成0到n-1的全排列,我们按照上述回溯法编写。 但是,引用[1]的方法是通过固定位数(n位)然后每一位从0到9递归,然后打印时去掉前导0,但这里我们只需要0到n-1,且每个数字只出现一次,所以不能直接使用。 因此,我们采用标准的回溯法。 示例:当n=3时,排列为: 0,1,2 0,2,1 1,0,2 1,2,0 2,0,1 2,1,0 下面给出C++代码实现: ```cpp #include <iostream> #include <vector> using namespace std; void generate(int n, int index, vector<int>& perm, vector<bool>& used) { if (index == n) { // 打印当前排列 for (int i = 0; i < n; i++) { cout << perm[i] << " "; } cout << endl; return; } for (int d = 0; d < n; d++) { if (!used[d]) { used[d] = true; perm[index] = d; generate(n, index+1, perm, used); used[d] = false; // 回溯 } } } void generate_permutations(int n) { if (n <= 0) return; vector<bool> used(n, false); // 初始化n个false vector<int> perm(n); // 排列数组 generate(n, 0, perm, used); } int main() { int n; cout << "请输入n: "; cin >> n; generate_permutations(n); return 0; } ``` 另外,引用[2]提到了序数法,但这种方法通常用于按顺序生成排列,且不易于直接实现所有排列的生成(尤其是对于全排列的生成,递归回溯更直观)。因此,我们这里使用递归回溯法。 注意:当n较大时,全排列的数量是n!,会非常大,所以实际运行可能很慢。 如果不想使用递归,我们可以使用迭代的方法,比如使用栈来模拟递归,或者使用非递归的生成算法(如字典序法)。但递归方法最简单直观。 因此,我们提供了递归回溯法来生成0到n-1的全排列。 如果用户需要非递归方法,我们可以进一步补充。 现在,根据用户的问题,我们给出了生成全排列递归方法。 相关问题: 1. 如何用非递归方法生成全排列? 2. 如何生成有重复元素的全排列? 3. 如何按字典序输出全排列? </think> ### 生成0到n-1的全排列方法 生成0到n-1的全排列需要使用**回溯法**,核心思路是通过递归遍历所有可能的数字组合,并通过标记数组避免重复使用数字。以下是详细步骤代码实现: #### 算法步骤 1. **初始化**:创建结果数组`perm`存储当前排列,布尔数组`used`标记数字是否已使用 2. **递归生成**: - 从索引0开始递归 - 对每个位置尝试所有未使用的数字(0到n-1) - 标记已使用的数字,递归下一位置 - 回溯时取消标记(关键步骤) 3. **终止条件**:当处理完所有位置(当前索引=n)时保存结果 4. **重复处理**:确保每次递归都尝试所有可用数字 #### C++代码实现 ```cpp #include <iostream> #include <vector> using namespace std; // 递归生成全排列 void generate_permutations(int n, int index, vector<int>& perm, vector<bool>& used) { if (index == n) { // 终止条件:完成一个排列 for (int num : perm) cout << num << " "; cout << endl; return; } for (int d = 0; d < n; d++) { if (!used[d]) { // 仅使用未标记的数字 used[d] = true; // 标记使用 perm[index] = d; // 存入当前排列 generate_permutations(n, index + 1, perm, used); // 递归下一位置 used[d] = false; // 回溯:取消标记 } } } // 主函数 void print_all_permutations(int n) { if (n <= 0) return; vector<bool> used(n, false); // 标记数组(初始未使用) vector<int> perm(n); // 存储当前排列 generate_permutations(n, 0, perm, used); } // 示例调用 int main() { print_all_permutations(3); /* 输出: 0 1 2 0 2 1 1 0 2 1 2 0 2 0 1 2 1 0 */ } ``` #### 关键说明 1. **时间复杂度**:$O(n!)$(全排列数量) 2. **空间复杂度**:$O(n)$(递归栈深度) 3. **去重机制**:`used`数组确保每个数字只用一次 4. **回溯核心**:递归返回后执行`used[d]=false`释放数字 5. **输出顺序**:按数字升序自然生成(字典序最小) > 示例:当n=3时,算法首先生成`0,1,2`,回溯到第二位尝试`0,2,1`,再回溯到第一位尝试`1,0,2`等,直到所有排列生成[^1]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值