输入一个数 \(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;
  • stackn 被定义为全局变量。
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_北航数据结构:输出全排列的更多相关文章

  1. PTA数据结构 习题2.8 输出全排列 (20分)

    习题2.8 输出全排列 (20分) 请编写程序输出前n个正整数的全排列(n<10),并通过9个测试用例(即n从1到9)观察n逐步增大时程序的运行时间. 输入格式: 输入给出正整数n(<10 ...

  2. PTA 输出全排列(20 分)

    7-2 输出全排列(20 分) 请编写程序输出前n个正整数的全排列(n<10),并通过9个测试用例(即n从1到9)观察n逐步增大时程序的运行时间. 输入格式: 输入给出正整数n(<10). ...

  3. java 循环移位输出全排列

    //题目:利用1.2.2.3.4这4个数字,用java写一个main函数打印出所有不同的排列,如12234,,2234等,要求打印出来不能有重复 1 package test123; 2 3 impo ...

  4. [PHP] 数据结构-输出链表倒数第k个结点PHP实现

    输入一个链表,输出该链表中倒数第k个结点.第一个指针走(k-1)步,到达第k个节点,两个指针同时往后移动,当第一个结点到达末尾的时候,第二个结点所在位置就是倒数第k个节点了 <?php clas ...

  5. poj 1256 按一定顺序输出全排列(next_permutation)

    Sample Input 3aAbabcacbaSample Output AabAbaaAbabAbAabaAabcacbbacbcacabcbaaabcaacbabacabcaacabacbaba ...

  6. DFS输出全排列

    前言 输入n(1 <= n <= 20),按字典序输出所有1~n的排列.如果排列数量太多,则只需要输出前100个 输入样例 3 输出样例 1 2 3 1 3 2 2 1 3 2 3 1 3 ...

  7. java实现全排列输出

    java实现全排列输出 转自:http://easonfans.iteye.com/blog/517286 最近在找工作,面试java程序员或者软件工程师,在笔试的时候常常见到这么一道题:全排列 的输 ...

  8. 【STL】全排列生成算法:next_permutation

    C++/STL中定义的next_permutation和prev_permutation函数是非常灵活且高效的一种方法,它被广泛的应用于为指定序列生成不同的排列. next_permutation函数 ...

  9. 全排列问题(递归&非递归&STL函数)

    问题描述: 打印输出1-9的所有全排序列,或者打印输出a-d的全排列. 思路分析: 将每个元素放到余下n-1个元素组成的队列最前方,然后对剩余元素进行全排列,依次递归下去. 比如:1 2 3 为例首先 ...

随机推荐

  1. Unreal ListView使用篇

    应用 ListView,在Unreal UI界面开发中用途非常广泛,基本只要你使用列表,就得需要用ListView.比如排行榜100个列表,界面上只需要显示出10个,我们肯定不能生成100个ui实例, ...

  2. Python基础—函数(Day9)

    一.函数的定义 def 关键字,定义一个函数 my_len 函数名(书写规则与变量名一样) def与函数名中间一个空格. def与函数名中间一个空格. 函数名():加冒号 函数体 my_len()#函 ...

  3. Vue2.0源码学习(2) - 数据和模板的渲染(下)

    vm._render是怎么实现的 上述updateComponent方法调用是运行了一个函数: // src\core\instance\lifecycle.js updateComponent = ...

  4. 我的平安夜-Merry Christmas

    我的平安夜-Merry Christmas 平安夜给自己买的第一个"苹果",嘻嘻. 今夜,不想去学习技术知识点什么的, 我们就想到哪里写哪里,就简单聊聊思维方式吧. 其实我不想做今 ...

  5. RTP包中timestamp的间隔问题

    概述 近期在和同事调试G729的编解码库时碰到一个语音质量的问题,问题产生的原因和RTP包中的时间戳设置有关,特此记录下来. 问题现象,1001和1002账号注册在fs,媒体设置为G729并通过fs中 ...

  6. Spring5基础

    基于Spring 5.2.6 版本. Spring概念 IOC容器 IOC底层原理的演进过程--本质就是为了高内聚,低耦合 在原始方式中,我们通过new创建对象来实现创建对象的逻辑,但是这样做当对象路 ...

  7. 什么是Ajax?全面了解

    一:Ajax 引入Ajax: 我们知道,前端页面想要和后端进行数据交互,可以通过以下方式 将参数添加到url中,后端通过get方式从url中获取数据 GET请求 前端页面通过form表单,将数据以ge ...

  8. 技术管理进阶——为什么Leader的话有时候你听不懂

    原创不易,求分享.求一键三连 Hi,各位亲爱的小伙伴,小钗公号遵循日复盘->周复盘->月复盘->季度复盘->年总结策略,所以某类型文章到后期才会成体系. 今天这篇文章属于「月复 ...

  9. 【C# 锁】SpinWait结构 -同步基元|同步原语

    锁提供了线程安全三要素中的 有序性.二.SpinWait是同步锁的核心,而Thread是SpinWait的核心. Spinwait结构 完成代码:SpinWait.cs (dot.net) SpinW ...

  10. vmware扩容centos根目录

    在vmware中编辑,给磁盘扩容 在centos中使用命令fdisk /dev/sda 输入n创建新分区 输入p创建主分区 回车,默认分区号 回车,默认起始扇区 回车,默认last扇区 输入t,改变分 ...