题目描述
把 1~n
这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。
输入格式
一个整数n。
输出格式
按照从小到大的顺序输出所有方案,每行1个。
首先,同一行相邻两个数用一个空格隔开。
其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。
数据范围
1(leq) n (leq) 9
输入样例
3
输出样例
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
思路
我们尝试用递归的方式解决:先输出所有以1开头的排列(这一步是递归调用),然后输出以2开头的排列(又是递归调用),接着是以3开头的排列······最后才是以n开头的排列。
以1开头的排列的特点是:第一位是1,后面是2~9
的排列。根据字典序的定义,这些2~9
的排列也必须按照字典序排列。不过需要注意的是,在输出时,每个排列的最前面要加上1
。这样一来,所设计的递归函数需要以下参数:
- 已经确定的“前缀”序列,以便输出。
- 需要进行全排列的元素集合,以便依次选做第一个元素。
因此我们可以得到下面的递归函数:
void dfs(int u,int ans)
{
if(ans==n)//递归边界
{
for(int i=0;i<n;i++)
cout<<stu[i]<<" ";
cout<<endl;
return;
}
int i=1,j=1;
for(i=1;i<=n;i++) //尝试在stu[u]中填写各种整数i
{
for(j=0;j<ans;j++)
{
if(stu[j]==i)//如果i已经在stu[0]~stu[u-1]出现过,则不能再选
{
break;
}
}
if(j==ans)
{
stu[ans]=i;
dfs(i,ans+1);//递归调用
}
}
}
循环变量i是当前考察的stu[ans]。为了检查元素i是否已经用过,我们需要内层循环中加入一个判断,如果发现有某个stu[j] == i时,则break(跳出内层循环)。如果最终j == ans,则说明i没有在序列中出现过,把它添加到序列末尾后递归调用。
递归C++ 代码
#include<iostream>
using namespace std;
int n;
int stu[10];
void dfs(int u,int ans)
{
if(ans==n)//递归边界
{
for(int i=0;i<n;i++)
cout<<stu[i]<<" ";
cout<<endl;
return;
}
int i=1,j=1;
for(i=1;i<=n;i++) //尝试在stu[u]中填写各种整数i
{
for(j=0;j<ans;j++)
{
if(stu[j]==i)//如果i已经在stu[0]~stu[u-1]出现过,则不能再选
{
break;
}
}
if(j==ans)
{
stu[ans]=i;
dfs(i,ans+1);//递归调用
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
dfs(1,0);
return 0;
}
递归Java代码
import java.util.Scanner;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main {
static int[] stu=new int[10];
static int[] vis=new int[10];
static int n;
static PrintWriter out = new PrintWriter(System.out);
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
dfs(1,0);
out.flush();
}
public static void dfs(int u,int ans)
{
if(ans==n)//递归边界
{
for(int i=0;i<n;i++)
out.print(stu[i]+" ");
out.println();
return;
}
int i=1,j=1;
for(i=1;i<=n;i++) //尝试在stu[u]中填写各种整数i
{
if(vis[i]==0)
{
vis[i]=1;
stu[ans]=i;
dfs(i,ans+1);//递归调用
vis[i]=0;
}
}
}
}