BUAA_DS_北航数据结构:输出全排列
输入一个数 \(n\),输出 \(1\sim n\) 的所有全排列,每个排列占一行,每个字符保留 \(5\) 个场宽。勤奋的同学一定已经开始打表了是吧。
说是能做肯定不是骗大家,那怎么做呢~
其实回溯法本质还是递归,回想我们做过的小兔子(青蛙)跳台阶的那题,只是需要算出总的方案数就可以,但是这个让你来输出具体的排列,这就需要你来保留每一层递归的状态,所以我们用一个全局数组来完成这一工作(暂且命名为stack,大家可以查一查这个单词什么意思)。
以 \(n=3\) 为例,一开始stack 是空的,我们需要枚举从 \(1\) 到 \(n\) 这几个数字,填充到 stack[0] 里:
| \(\Downarrow\) | ||
|---|---|---|
| 1 | 
OK 放进去了,然后考虑后面一个数字,还是从 \(1\) 到 \(n=3\) 枚举:
| \(\Downarrow\) | ||
|---|---|---|
| 1 | 1 | 
大家觉得这样行吗?
——你这都跟前面重复了肯定不行啊!
不行是吧,那就换一个 \(2\):
| \(\Downarrow\) | ||
|---|---|---|
| 1 | 2 | 
OK 舒服了,再看下一个(我已经试过了,放 \(1\) 不行,\(2\) 也不行,那就放 \(3\)):
| \(\Downarrow\) | ||
|---|---|---|
| 1 | 2 | 3 | 
好的,看看下一个……哎呦怎么满了,那就输出一下叭:
此时你的控制台输出:
    1    2    3
好的我们把 \(3\) 拿掉,往回退一层:
| \(\Downarrow\) | ||
|---|---|---|
| 1 | 2 | 
这个 \(2\) 不是从 \(1\) 枚举过来的吗,那还没有枚举完,就还要继续枚举下去:
| \(\Downarrow\) | ||
|---|---|---|
| 1 | 3 | 
好的进入下一层从 \(1\) 开始枚举:
| \(\Downarrow\) | ||
|---|---|---|
| 1 | 3 | 1 | 
不行,跟前面重复了,换 \(2\) 试试:
| \(\Downarrow\) | ||
|---|---|---|
| 1 | 3 | 2 | 
好像可以,stack 又满了诶,输出一下:
    1    2    3
    1    3    2
再把最后一个元素试图换一下 \(3\):
| \(\Downarrow\) | ||
|---|---|---|
| 1 | 3 | 3 | 
不行,死心了,往回退:
| \(\Downarrow\) | ||
|---|---|---|
| 1 | 3 | 
呃,第二个元素也举到 \(3\) 了,不能继续举了,只能再退一层,把 \(1\) 换成 \(2\) 试试:
| \(\Downarrow\) | ||
|---|---|---|
| 2 | 
没啥问题,再放下一个,从 \(1\) 开始枚举:
| \(\Downarrow\) | ||
|---|---|---|
| 2 | 1 | 
也没有重复的,再下一个:
| \(\Downarrow\) | ||
|---|---|---|
| 2 | 1 | 1 | 
最后一个放 \(1\) 行吗?不行,重复了。
| \(\Downarrow\) | ||
|---|---|---|
| 2 | 1 | 2 | 
最后一个放 \(2\) 也重复了。
| \(\Downarrow\) | ||
|---|---|---|
| 2 | 1 | 3 | 
那就放 \(3\),一看可以,输出一下:
    1    2    3
    1    3    2
    2    1    3
接下来就是周而复始的过程,直到全部都枚举完,你的输出应该是这样的:
    1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1
我们习惯上把上面的递归过程叫做 \(DFS(Depth-First-Search)\),中文叫“深度优先搜索”,就是在一层递归里面有一个循环,每个循环还要跑一个递归,关于 \(DFS\) 在数据结构的“图 \((Graph)\)” 部分也会有讲。用“伪代码”(新词汇又来了hhh)描述这个递归是这样的:
- 参数 len是进入递归之前stack的有效长度,所以最开始调用的时候len应该是 0;
- stack和- n被定义为全局变量。
void dfs(int len) {
	if (len == n) {
		输出 stack;
		return;
	}
	for (int i = 1; i <= n; i++)
		if (i 与 stack[0] 到 stack[len - 1] 的元素都不重复) {
			stack[len] = i;
			dfs(len + 1);
		}
}
对于某一层递归,进入之前已经填充了 len 个,所以检查重复的时候只要从 stack[0] 检查到 stack[len - 1]。
可以想见在运行的时候这个 stack 应该是不断伸伸缩缩的,但是在退回上一层的时候并没有删除后面的元素(反正留在那也不影响,因为检查重复是到下标 len - 1,后面的不会被访问到)。
在 main() 函数里只需要这么写:
int main() {
    scanf("%d", &n);
    dfs(0);
    // 没错我的代码可以很潇洒
    return 0;
}
然后再把“输出 stack”翻译一下:
void print_stack() {
    for (int i = 0; i < n; i++) {
        printf("%5d", stack[i]);
    }
    puts("");
}
翻译一下“i 与 stack[0] 到 stack[len - 1] 的元素是否重复”,顺序查找,找到了返回 1,找不到返回 0:
int find_in_stack(int key, int len) {
    for(int i = 0; i < len; i++) {
        if (key == stack[i])
            return 1;
    }
    return 0;
}
大功告成,奉上完整代码:
#include <stdio.h>
int n;
int stack[10];
void print_stack();
void dfs(int len);
int find_in_stack(int key, int len);
int main() {
    scanf("%d", &n);
    dfs(0);
    // 没错我的代码可以很潇洒
    return 0;
}
void dfs(int len) {
    if (len == n) {
        print_stack();
        return;
    }
    for (int i = 1; i <= n; i++)
        if (!find_in_stack(i, len)) {
            stack[len] = i;
            dfs(len + 1);
        }
}
void print_stack() {
    for (int i = 0; i < n; i++)
        printf("%5d", stack[i]);
    puts("");
}
int find_in_stack(int key, int len) {
    for(int i = 0; i < len; i++)
        if (key == stack[i])
            return 1;
    return 0;
}
BUAA_DS_北航数据结构:输出全排列的更多相关文章
- PTA数据结构 习题2.8 输出全排列 (20分)
		习题2.8 输出全排列 (20分) 请编写程序输出前n个正整数的全排列(n<10),并通过9个测试用例(即n从1到9)观察n逐步增大时程序的运行时间. 输入格式: 输入给出正整数n(<10 ... 
- PTA 输出全排列(20 分)
		7-2 输出全排列(20 分) 请编写程序输出前n个正整数的全排列(n<10),并通过9个测试用例(即n从1到9)观察n逐步增大时程序的运行时间. 输入格式: 输入给出正整数n(<10). ... 
- java 循环移位输出全排列
		//题目:利用1.2.2.3.4这4个数字,用java写一个main函数打印出所有不同的排列,如12234,,2234等,要求打印出来不能有重复 1 package test123; 2 3 impo ... 
- [PHP] 数据结构-输出链表倒数第k个结点PHP实现
		输入一个链表,输出该链表中倒数第k个结点.第一个指针走(k-1)步,到达第k个节点,两个指针同时往后移动,当第一个结点到达末尾的时候,第二个结点所在位置就是倒数第k个节点了 <?php clas ... 
- poj 1256 按一定顺序输出全排列(next_permutation)
		Sample Input 3aAbabcacbaSample Output AabAbaaAbabAbAabaAabcacbbacbcacabcbaaabcaacbabacabcaacabacbaba ... 
- DFS输出全排列
		前言 输入n(1 <= n <= 20),按字典序输出所有1~n的排列.如果排列数量太多,则只需要输出前100个 输入样例 3 输出样例 1 2 3 1 3 2 2 1 3 2 3 1 3 ... 
- java实现全排列输出
		java实现全排列输出 转自:http://easonfans.iteye.com/blog/517286 最近在找工作,面试java程序员或者软件工程师,在笔试的时候常常见到这么一道题:全排列 的输 ... 
- 【STL】全排列生成算法:next_permutation
		C++/STL中定义的next_permutation和prev_permutation函数是非常灵活且高效的一种方法,它被广泛的应用于为指定序列生成不同的排列. next_permutation函数 ... 
- 全排列问题(递归&非递归&STL函数)
		问题描述: 打印输出1-9的所有全排序列,或者打印输出a-d的全排列. 思路分析: 将每个元素放到余下n-1个元素组成的队列最前方,然后对剩余元素进行全排列,依次递归下去. 比如:1 2 3 为例首先 ... 
随机推荐
- 在ABP VNext框架中处理和用户相关的多对多的关系
			前面介绍了一些ABP VNext架构上的内容,随着内容的细化,我们会发现ABP VNext框架中的Entity Framework处理表之间的引用关系还是比较麻烦的,一不小心就容易出错了,本篇随笔介绍 ... 
- suse 12 二进制部署 Kubernetets 1.19.7 - 第12章 - 部署dashboard插件
			文章目录 1.12.0.创建namespace 1.12.1.创建Dashboard rbac文件 1.12.2.创建dashboard文件 1.12.3.查看pod以及svc 1.12.4.获取 d ... 
- 利用终端统计 Xcode代码数量命令
			直接利用终端命令进入工程文件夹然后写下或复制以下命令 find . -name "*.m" -or -name "*.h" -or -name "*. ... 
- 记一次慢查询优化sql
			sql语句优化(慢查询日志) 最近,旧系统向新系统迁移工程刚刚结束.开发完成后,测试阶段也是好好休息了一把.接到一个需求,由于内部员工使用的网站部分功能加载时间很长,所以需要去优化系统的一些功能.大致 ... 
- 小牟有趣的PWN
			咳咳,主要是记一下最近学二进制然后工作室里面一个一起学pwn,然后遇到的一个比较好玩的题目. 一共呢,是两个文件,这也是最近学习pwn第一次做到两个文件的题目, 如果想要源文件,这边可以加我们的工作室 ... 
- 库存数量管理方案一:基于SQL存储过程和MERGE(结合活字格案例)
			库存更新是ERP系统的基本功能,一般包括以下动作:1.以库位编号和商品编号查询库存表,如果查询不到,则添加一行库存信息,如:(出入库)库位编号/(出入库)商品编号/(出入库)+或-数量2.以库位编号和 ... 
- 【windows 操作系统】进程
			前言 Windows的内部实现也近似于"一切皆文件"的思想,当然,这一切都只在内核里才有,下载一个WinObj这软件就可以看到,Windows上各种设备.分区.虚拟对象都是挂载到根 ... 
- vue如何全局引用公共js
			在项目开发中需要调用一些工具类方法,所以需要将公共方法放在公共js中,并且需要全局引用这些公共js 1:创建公共JS(utils.js) src/common/utils.js export def ... 
- 初识python(2)
			目录 引言 数据类型 字典 集合 元组 布尔值 用户交互 格式化输出 运算符 增量赋值 链式赋值 交叉赋值 解压赋值 逻辑运算符 成员运算符 身份运算符 引言 小伙伴们昨天已经讲了一点python的数 ... 
- LeetCode-007-整数反转
			整数反转 题目描述:给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果. 如果反转后整数超过 32 位的有符号整数的范围 [\(−2^{31}\), \(2^{31}\) − ... 
