生成n个数的全排列【递归、回溯】
下面讨论的是n个互不相同的数形成的不同排列的个数。
毕竟,假如n个数当中有相同的数,那n!种排列当中肯定会有一些排列是重复的,这样就是一个不一样的问题了。 /*=====================================
数的全排列问题。将n个数字1,2,…n的所有排列枚举出来。
2 3 1
2 1 3
3 1 2
3 2 1 思路:
递归函数定义如下:
void fun(int a[],int flag[],int i,int ans[]);
//原始数据存放于a[]。flag[]的每一个元素标记a[]对应位置处的元素是否已经被选用。
//i表示当前对某排列而言,正在寻找到第i个数据。
//ans[]是存放每一个排列的地方。每当放够n个元素到ans则开始打印该数组。 程序中先把原始数据读入到数组a[]。flag[]数组元素全部置0.
生成一个排列的过程可以这样认为:
每次递归都扫描flag[],寻找一个flag[i]==0然后把a[i]选到排列当中。
(也即放到ans[i]当中。选a[i]后要把flag[i]置1.)
这样就确定了当前排列的第i个元素。
然后递归进入下一层确定第i+1个数。
以此类推,逐层递归直至i==n-1(因为i从0开始,所以是i==n-1)就认为已经得到了一个
完整的排列。这个时候可以把ans数组输出了。
输出后可以开始回溯了。
每次回溯都要把flag[i]置0以便还原现场。(相当于ans[i]不选a[i]了。)
置0后继续循环枚举当前位置的其他可能取值。 ======================================*/
下面是我自己按照自己的理解做的,其实有点浪费空间了:
#include<stdio.h>
int n,count;//n表示参与排列的数据的个数,count表示不同排列的个数
void fun(int a[],int flag[],int i,int ans[]);
//原始数据存放于a[]。flag[]的每一个元素标记a[]对应位置处的元素是否已经被选用。
//i表示当前对某排列而言,正在寻找到第i个数据。
//ans[]是存放每一个排列的地方。每当放够n个元素到ans则开始打印该数组。
int main()
{
int i;
int a[],flag[],ans[];
freopen("5.out","w",stdout);
scanf("%d",&n);
for(i=;i<n;i++)
{
flag[i]=;
a[i]=i+;
}
count=;
fun(a,flag,,ans);//从第0号开始出发,依次确定每一个位置的数值
printf("total:%d\n",count);
return ;
}
void fun(int a[],int flag[],int i,int ans[])
{
int j,k;
if(i==n-)
{
for(j=;j<n;j++)
{
if(flag[j]==)//找到了一个排列,把这个排列给输出。
{
ans[i]=a[j];
for(k=;k<n;k++) printf("%d ",ans[k]);
printf("\n");
count++;
return ;
}
}
}
else
{
for(j=;j<n;j++)
{
if(flag[j]==)
{
ans[i]=a[j]; flag[j]=;
fun(a,flag,i+,ans);
flag[j]=;
}
}
}
}
-----------------------------------------------------插入分割线-----------------------------------------------------
代码OJ测试:http://ica.openjudge.cn/dg3/solution/10102944/
题目要求:原序列是字母,而且字母不重复,而且原序列按字典序升序排列,要求生成的所有排列按字典序升序输出。
AC代码:(把上面的代码做简单修改即可)
#include<stdio.h>
#include<string.h> int n,count;//n表示参与排列的数据的个数,count表示不同排列的个数
void fun(char a[],int flag[],int i,char ans[]);
//原始数据存放于a[]。flag[]的每一个元素标记a[]对应位置处的元素是否已经被选用。
//i表示当前对某排列而言,正在寻找到第i个数据。
//ans[]是存放每一个排列的地方。每当放够n个元素到ans则开始打印该数组。 int main()
{
int i;
int flag[];
char a[],ans[];
scanf("%s",a);
//scanf("%d",&n);
n=strlen(a);
for(i=;i<n;i++)
{
flag[i]=;
//a[i]=i+1;
}
count=;
fun(a,flag,,ans);//从第0号开始出发,依次确定每一个位置的数值
//printf("total:%d\n",count);
return ;
}
void fun(char a[],int flag[],int i,char ans[])
{
int j,k;
if(i==n)
{
for(k=;k<n;k++) printf("%c",ans[k]);
printf("\n");
count++;
return ;
}
else
{
for(j=;j<n;j++)
{
if(flag[j]==)
{
ans[i]=a[j]; flag[j]=;
fun(a,flag,i+,ans);
flag[j]=;
}
}
}
}
-----------------------------------------------------分割线结束-----------------------------------------------------
下面是书上的代码,比较好一点,不过一开始可能不好理解。建议看懂了上面的再来看这段代码。
#include<stdio.h>
int n,a[];
long count=;
void fun(int k); //fun(k)表示现在正在确定某个排列当中的第k个数
//也可以认为fun(k)是生成第k个数到第n个数的所有排列。
int main()
{
int i;
scanf("%d",&n);
for(i=;i<n;i++)
{
a[i]=i+;
}
fun();
printf("total:%d\n",count);
return ;
}
void fun(int k)//fun(k)表示现在正在确定某个排列当中的第k个数
{
int j,t;
if(k==n)
{
count++;
for(j=;j<n;j++) printf("%d ",a[j]);
printf("\n");
return ;
}
else
{
for(j=k;j<n;j++)//注意:这里是在原始数组内交换数据实现排列,所以j从k开始
{
t=a[k];a[k]=a[j];a[j]=t;
fun(k+);
t=a[k];a[k]=a[j];a[j]=t;
}
}
}
下面是网上对这个问题的代码,也不错。
#include <stdio.h> void print(int array[], int end);
void swap(int &a, int &b);
void permute(int array[], int begin, int end); void permute(int array[], int begin, int end)
{
if (begin == end)
{
print(array, end);
}
else
{
for (int j = begin; j <= end; ++j)
{
swap(array[j], array[begin]);
permute(array, begin+, end); //recursive,enlarge problem's scope
swap(array[j], array[begin]); //backtracking
}
}
return;
} void print(int array[], int end)
{
for (int i = ; i <= end; ++i)
{
fprintf(stdout, "%d", array[i]);
if (i != end)
{
fprintf(stdout, "\t");
}
}
fprintf(stdout, "\n");
return;
} void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
return;
} int main()
{
int array[]={,,,};
permute(array,,);
return ;
}
/*==============================================================================
下面的分析来自王晓东编著的《算法设计与分析(第2版)》,清华大学出版社出版 ,
第2章递归与分治策略例题2.4排列问题。
设R={r1,r2,r3,...,rn}是要进行排列的n个元素,Ri=R-{ri}。
集合X中的元素的全排列记为perm(X)。(ri)perm(X)表示在全排列perm(X)的每一个
排列前加上前缀ri得到的排列。R的全排列可归纳定义如下:
当n=1时,perm(R)=(r),其中r是集合R中唯一的一个元素。
当n>1时,perm(R)由(r1)perm(R1),(r2)perm(R2),...... ,(rn)perm(Rn)构成。
依此递归定义,可以设计产生perm(R)的递归算法如下: (详见下面代码) ================================================================================*/
public static void perm(Object[] List,int k,int m)
{//产生list[k:m]的所有排列
if(k==m)
{//只剩一个元素
for(int i=0;i<=m;i++)
System.out.print(List[i]);
System.out.println();
}
else
{
//还有更多个元素,递归产生排列
for(int i=k;i<=m;i++)
{
MyMath.swap(List,k,i);
perm(List,k+1,m);
MyMath.swap(List,k,i);
}
}
}
/*============================================================================================
算法perm(list,k,m)递归地产生所有前缀是list[0:k-1]且后缀是list[k:m]的全排列的所有排列。
调用算法perm(list,0,n-1)则产生list[0:n-1]的全排列。
在一般情况下,k<m。算法将list[k:m]中每一个元素分别与list[k]中的元素交换,然后递归地计算list[k+1:m]的全排列,
并将结果作为list[0:k]的后缀。
MyMath.swap()用于交换两个表元素值。
==============================================================================================*/
其实这个算法和上述第二段代码是一个道理。
不管如何修改,采用递归的设计方法实现的全排列,效率都不高。下面看看书上另外写的非递归代码。
(下次再见呵呵)
生成n个数的全排列【递归、回溯】的更多相关文章
- 【C/C++】n皇后问题/全排列/递归/回溯/算法笔记4.3
按常规,先说一下我自己的理解. 递归中的return常用来作为递归终止的条件,但是对于返回数值的情况,要搞明白它是怎么返回的.递归的方式就是自己调用自己,而在有返回值的函数中,上一层的函数还没执行完就 ...
- 递归回溯生成和解决数独问题c/c++
数独 程序地址https://github.com/papicheng/blog/tree/master/%E6%95%B0%E7%8B%AC 一.游戏规则介绍: 数独是源自18世纪瑞士的一种数学游戏 ...
- 递归回溯 UVa140 Bandwidth宽带
本题题意:寻找一个排列,在此排序中,带宽的长度最小(带宽是指:任意一点v与其距离最远的且与v有边相连的顶点与v的距离的最大值),若有多个,按照字典序输出最小的哪一个. 解题思路: 方法一:由于题目说结 ...
- C语言实现全排列和回溯法总结
一.递归实现全排列 #include"cstdio" ]; void print_permutation(int n,int *A,int cur){ if(cur==n){ ;i ...
- 剑指Offer——全排列递归思路
剑指Offer--全排列递归思路 前言 全排列,full permutation, 可以利用二叉树的遍历实现.二叉树的递归遍历,前中后都简洁的难以置信,但是都有一个共同特点,那就是一个函数里包含两次自 ...
- LeetCode || 递归 / 回溯
呜呜呜 递归好不想写qwq 求“所有情况”这种就递归 17. Letter Combinations of a Phone Number 题意:在九宫格上按数字,输出所有可能的字母组合 Input: ...
- 40. 组合总和 II + 递归 + 回溯 + 记录路径
40. 组合总和 II LeetCode_40 题目描述 题解分析 此题和 39. 组合总和 + 递归 + 回溯 + 存储路径很像,只不过题目修改了一下. 题解的关键是首先将候选数组进行排序,然后记录 ...
- Lua在给定范围内,生成指定个数不重复随机数组
本篇主要是参考 lua连续随机数 这篇文章完成.大家可以去原贴查看学习. 生成随机数组,暂时发现两种方法 1.把生成的数放到一个表里面,每次随机时判断这个表里是否有,若有再随机一次(问了朋友,很多人都 ...
- 生成32位UUID及生成指定个数的UUID
参考地址:https://blog.csdn.net/xinghuo0007/article/details/72868799 UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯 ...
随机推荐
- codeforces Educational Codeforces Round 16-E(DP)
题目链接:http://codeforces.com/contest/710/problem/E 题意:开始文本为空,可以选择话费时间x输入或删除一个字符,也可以选择复制并粘贴一串字符(即长度变为两倍 ...
- java作业6
super用法: 1. 子类的构造函数如果要引用super的话,必须把super放在函数的首位 代码如下: class Base { Base() { System.out.println(" ...
- 根据存放位置数据的链表P打印链表L的元素
题目:给定一个链表L和另一个链表P,它们包含以升序排列的整数.操作printLots打印L中那些由P所指定的位置上的元素.写出过程printLots(L,P).只可以使用公有的STL容器操作.该过程的 ...
- <area> 标签
定义和用法 <area> 标签定义图像映射中的区域(注:图像映射指得是带有可点击区域的图像). area 元素总是嵌套在 <map> 标签中. 注释:<img> 标 ...
- css中解决img在a元素中使得a元素撑大写法
给外面a标签padding-left:; img自身float:left;margin-left:; 这种写法避免了不少因浮动产生的问题,且代码简明,推荐适当的使用此方法.
- 线性函数拟合R语言示例
线性函数拟合(y=a+bx) 1. R运行实例 R语言运行代码如下:绿色为要提供的数据,黄色标识信息为需要保存的. x<-c(0.10,0.11, 0.12, 0.13, 0.14, ...
- Trinity 安装
http://trinityrnaseq.github.io/ 安装包下载地址: https://github.com/trinityrnaseq/trinityrnaseq/releases 解压 ...
- 第二部分:python 常用操作与函数
2.1,交换两数据>> a,b = b,a 2.2,去掉list中的重复元素>> list1 = [1,2,3,2,3,2,5,6]>> list1 = list( ...
- 灰度图像的自动阈值分割(Otsu 法)(转载)
灰度图像的自动阈值分割(Otsu 法) 机器视觉领域许多算法都要求先对图像进行二值化.这种二值化操作阈值的选取非常重要.阈值选取的不合适,可能得到的结果就毫无用处.今天就来讲讲一种自动计算阈值的方法. ...
- SQL编写
//用户表,用户ID,用户名称create table t_user (user_id int,username varchar(20));//用户帐户表,用户ID,用户余额(单位分)create t ...