如果你对其他算法或者案例感兴趣,请考虑阅读我的以下文章。
递归案例-汉诺塔.
递归案例-正整数划分.
动态规划案例-矩阵连乘(含表格填写、问题理解、实例解决).
动态规划案例-最长公共子序列(含表格填写、问题理解、实例解决、例题答案).
递归案例-电路布线(含表格填写等超详细,纯人话讲解).
问题
设计一个递归算法生成n个元素{r1,r2,…,rn}的全排列。
eg:生成3个元素的全排列:{(1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,1,2),(3,2,1)}
问题分析
该类问题的解决方法有两种:1,固定位置找元素 2,固定元素找位置
本例采用固定位置找元素的方法来解决这个问题。
以数组a[]={1,2,3}为例,现要生成这三个元素的全排列。
1.我们放入第一个位置上的元素,我们有三个选择,可以放a[0],a[1],a[2],我们先放入a[0],也就是1
2.我们放入第二个位置上的元素,我们有两个选择,可以放a[1]或者a[2]
3.我们放入第三个位置上的元素,只剩下一个选择。
4-6步的结果为:
7-9步的结果为:
算法设计
为什么要用递归
递归的定义:函数调用自身。
我认为运用递归的条件就是:每一步进行的操作基本相同,并且问题规模逐渐减小。
通过上面的例子,首先确定第一个位置上的元素,将问题规模为n分解为3个问题规模为n-1的子问题,其次每一个子问题进行的操作基本相同,都是在找元素。
递归函数参数的设计
从上面的问题分析中,我们可以得出3个很重要的元素:数据数组(存储数据)、位置信息(来确定当前操作的是第几个位置)、数组数据个数(用来判断是否排列到最后一个位置)
我们来构造fun(int a[],int I,int n)函数,其中三个参数分别为:a[]存储数据的数组、i为当前位置的序号、n为数组的个数-1
算法分析
1.首先我们要确定第一个位置上的元素,第一个位置上的元素可以是数组数据中的任何一个,我们可以采用数组元素交换的方式来确定第一个位置上的元素。
2.当我们确定了第一个位置上的元素的时候,我们就需要调用递归函数,此时将位置信息+1,来保证我们开始确定第二个位置上的元素。依次下去,直到确定了最后一个元素。
3.在递归调用结束后,还需将数组数据元素交换成初始状态,我们重复第一步的操作,这样在最后一层递归完成之后就会依次将数组数据元素交换回来,使数组恢复成初始状态,方便我们进行下一个子问题的操作。
for(int j = i;j<=n;j++)
{
swap(a, i ,j);
fun(a, i+1, n);
swap(a, i ,j);
}
4.当遇到递归出口的时候,输出已经排列好的数组中的元素。
if(i==n)
{
for(int j = 0;j<=n;j++)
{
System.out.print(a[j]);
}
System.out.println();
}
代码演示
递归函数
public static void fun(int a[],int i,int n)
{
if(i==n)
{
for(int j = 0;j<=n;j++)
{
System.out.print(a[j]);
}
System.out.println();
}
else
for(int j = i;j<=n;j++)
{
swap(a, i ,j);
fun(a, i+1, n);
swap(a, i ,j);
}
}
交换函数
Java中注意交换,由于Java中没有指针,所以采用数组引用传递。
public static void swap(int a[],int i,int j)
{
int temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
主函数调用
public static void main(String[] args) {
int a[] = new int[]{1,2,3};
fun(a, 0, 2);
}