递归案例-全排列

如果你对其他算法或者案例感兴趣,请考虑阅读我的以下文章。

递归案例-汉诺塔.
递归案例-正整数划分.
动态规划案例-矩阵连乘(含表格填写、问题理解、实例解决).
动态规划案例-最长公共子序列(含表格填写、问题理解、实例解决、例题答案).
递归案例-电路布线(含表格填写等超详细,纯人话讲解).

问题

设计一个递归算法生成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

1
null
位置1
位置2
位置3
null
null
null

2.我们放入第二个位置上的元素,我们有两个选择,可以放a[1]或者a[2]

1
2
位置1
位置2
位置3
3
null
null

3.我们放入第三个位置上的元素,只剩下一个选择。

1
2
位置1
位置2
位置3
3
3
2

4-6步的结果为:

2
1
位置1
位置2
位置3
3
3
1

7-9步的结果为:

3
1
位置1
位置2
位置3
2
2
1

算法设计

为什么要用递归

递归的定义:函数调用自身。
我认为运用递归的条件就是:每一步进行的操作基本相同,并且问题规模逐渐减小。
通过上面的例子,首先确定第一个位置上的元素,将问题规模为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);
 }

输出结果

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章