C++ 全排列问题——递归交换法

对于求解全排列问题有最暴力的递归枚举法,但是我们希望可以优化时间,因此出现了递归交换法。

例题

洛谷1706

题目描述
输出自然数1到n所有不重复的排列,即n的全排列,要求所产生的任一数字序列中不允许出现重复的数字。

输入格式
一个整数n。

输出格式
由1~n组成的所有不重复的数字序列,每行一个序列。
每个数字保留 5个场宽。

输入样例

3

输出样例

    1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1

全排列问题——递归交换法

其实跟暴力枚举思路差不多,每次递归枚举第x个数字是几,之后a[x]可以选择不动,也可以选择与后面任意一个数交换位置,就是从后面选一个数放到x的位置上。

简而言之,就是每到一位就从后面选一个尚未被使用过的数字与该位数字交换,这里有些难理解,您可以自己按照程序推一下样例。

这样我们就可以打印所有的全排列了,但这样不是按顺序打印,所以这里需要每次对a[x] ~ a[n]进行排序。

举个例子,如对1、2、3进行全排列。当我们交换1和3后,序列变为3、2、1,如果说这里不排序,直接2、1都保持不动,就输出3、2、1了,可是我们先要的应该是3、1、2,所以要进行排序。

最后,算一下时间复杂度,我们发现需要从1到n一位一位的看,之后每位还要枚举x ~ n,所以总时间复杂度为O(n!)。

代码

# include <cstdio>
# include <cmath>
# include <cstring>
# include <algorithm>

using namespace std;

const int N_MAX = 10;

int n;
int a[N_MAX + 10];

void permutation(int x)
{
    if (x == n) {
        for (int i = 1; i <= n; i++)
            printf("%5d", a[i]);
        printf("\n");
        return;
    }
    for (int i = x; i <= n; i++) {
        sort(a + x, a + n + 1);
        swap(a[x], a[i]);
        permutation(x + 1);
        swap(a[x], a[i]);
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        a[i] = i;
    permutation(1);
    return 0;
}

原文地址:https://www.cnblogs.com/000zwx000/p/12335168.html

时间: 2024-08-01 20:09:51

C++ 全排列问题——递归交换法的相关文章

C++ 全排列问题——递归枚举法

全排列问题是一道非常经典的递归题目,而递归枚举法求解也是最暴力的一种方法. 例题 洛谷1706 题目描述 输出自然数1到n所有不重复的排列,即n的全排列,要求所产生的任一数字序列中不允许出现重复的数字. 输入格式 一个整数n. 输出格式 由1-n组成的所有不重复的数字序列,每行一个序列. 每个数字保留 5个场宽. 输入样例 3 输出样例 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 全排列问题--递归枚举法 这是一道经典的递归的题,每次递归枚举第x个数字是几,就是从1到

从全排列看递归

1.什么是递归 1)举个生活中的例子:假设有一项很繁重的工作,我们总能把它划分为:总工作量=今天的工作+剩下的工作,只要我们每天都在坚持,一点点继续,那么剩下的工作会越来越少,总有一天,我们可以实现我们的梦想!(程序员的自我安慰~) 2)数学上来看: 例如:n的阶乘 f(n) = n!,n为整数 f(n) = 1   n<= 1                       (1) n*f(n-1) n >1                  (2) 有一个基础部分:它包含一个或多个值,对这些值

n个整数全排列的递归实现(C++)

全排列是非常常用的一个小算法,下面是n个整数全排列的递归实现,使用的是C++ #include <iostream> using namespace std; int n = 0; void swap(char *a ,char *b) { int m ; m = *a; *a = *b; *b = m; } void perm(char list[],int k, int m ) { int i; if(k >m) { for(i = 0 ; i <= m ; i++) { co

二叉树非递归先中后序遍历 及 非递归交换二叉树两个孩子的位置

看到一个非递归交换一个二叉树的左右孩子的位置,于是想实现之,才发现非递归的先中后序遍历都忘记了……于是杂七杂八的写了一些,抄抄资料就实现了,然后实现非递归交换两个孩子的位置还是相当容易的.先直接上代码吧,其实这东西还是得自己写写过一遍的,印象才会更加深刻: #include <iostream> #include <fstream> #include <string> #include <stack> using std::cout; using std::

八皇后问题——递归+回溯法

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 高斯认为有76种方案.1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果. 求解过程: 采用遍历的办法,就是采用将每种情况都验证的办法最终找出问题的解,但是蛮力遍历的话,需要遍历的数据量太大,计算时间花费太大,所以在遍历

排序算法(交换法,选择法,插入排序,冒泡法,快速排序算法,C语言举例)

交换法:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动. 简单选择排序:的基本思想:第1趟,在待排序记录r[1]~r[n]中选出最小的记录,将它与r[1]交换;第2趟,在待排序记录r[2]~r[n]中选出最小的记录,将它与r[2]交换;以此类推,第i趟在待排序记录r[i]~r[n]中选出最小的记录,将它与r[i]交换,使有序序列不断增长直到全部排序完毕. 插入排序法:有一个已经有序的

C语言排序算法之简单交换法排序,直接选择排序,冒泡排序

C语言排序算法之简单交换法排序,直接选择排序,冒泡排序,最近考试要用到,网上也有很多例子,我觉得还是自己写的看得懂一些. 简单交换法排序 1 /*简单交换法排序 2 根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置 3 交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动 4 不稳定 5 */ 6 #include<windows.h> 7 #include<stdio.h> 8 void main(){ 9 int i,j,arr[10

全排列 (递归求解+字典序) java 转载

问题:给出一个字符串,输出所有可能的排列. 全排列有多种算法,此处仅介绍常用的两种:字典序法和递归法. 1.字典序法: 如何计算字符串的下一个排列了?来考虑"926520"这个字符串,我们从后向前找第一双相邻的递增数字,"20"."52"都是非递增的,"26 "即满足要求,称前一个数字2为替换数,替换数的下标称为替换点,再从后面找一个比替换数大的最小数(这个数必然存在),0.2都不行,5可以,将5和2交换得到"956

字符串全排列的递归实现

#include "stdafx.h" #include <iostream> #include <assert.h> using namespace std; void permutation(char *pStr, char* pBegin) { assert(pStr&&pBegin); if (*pBegin == '\0') cout << pStr << endl; else { for (char* pCh