(全排列)打印从1到最大的n位数

本文提供了一个C++程序实现,用于解决剑指Offer第12题:递归生成并打印所有由n位数字组成的数。通过递归调用,确保每个可能的数字组合都被打印出来,同时优化打印过程,避免输出前导零。

剑指offer第12题 

#include<iostream>
using namespace std;
void printRecur(char* number,int length,int index);
void print(char*number);
//递归打印
void PrintTotalMaxNumber(int n){
    if(n<=0)
        return ;
    char *number = new char(n+1);
    number[n] ='\0';
    for(int i = 0;i<10;++i){
        number[0] = '0'+i;
        printRecur(number,n,0);
    }
    delete [] number;
}
void printRecur(char*number,int length,int index){
    if(length - 1 == index){
        print(number);
        return ;
    }
    for(int i=index;i<10;i++){
        number[index+1] = '0' + i;
        printRecur(number,length,index+1);
    }
}
// 从第一位非0的开始打印。
void print(char* number){
    int i = 0;
    while(number[i] == '0')
        number++;
    cout<< number<< endl;

}
int main(){
    int n;
    cin>> n;
    PrintTotalMaxNumber(n);
    return 0;
}

 

<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]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值