1-N的整数的全排列等相关问题

全排列:对于给定的集合A{a1,...an},其中的n个元素互不相同,如何输出这n个元素的所有排列。

算法问题:

      1、求解1-N的全排列(按字典序输出)

      2、给出一个排列,求按字典序的下一个排列

      3、给出一个排列,求按字典序的上一个排列;

1解:递归算法求解全排列

这里以A{a,b,c}为例,来说明全排列的生成方法,对于这个集合,其包含3个元素,所有的排列情况有3!=6种,对于每一种排列,其第一个元素有3种选择a,b,c,对于第一个元素为a的排列,其第二个元素有2种选择b,c;第一个元素为b的排列,第二个元素也有2种选择a,c,……,依次类推,我们可以将集合的全排列与一棵多叉树对应。如下图所示


在此树中,每一个从树根到叶子节点的路径,就对应了集合A的一个排列。通过递归算法,可以在多叉树的构建过程,直接生成集合A的全排列,代码如下。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 /**
 4    交换数组中2元素,传递地址char*
 5 */
 6 int swap(char* arr, int i ,int j)
 7 {
 8     char temp = arr[i];
 9     arr[i] = arr[j];
10     arr[j] = temp;
11     return 0;
12 }
13 /**
14    1、递归函数
15    2、对数组arr,进行从index开始的全排列;
16 */
17 int fullPermutation(char* arr, int len, int index)
18 {
19     int i;
20     /**
21         if index == len ;
22         {
23             then 只对一个元素进行全排列,情况只有一种,输出即可
24             printf arr;
25             这也是函数结束条件。
26         }
27         else
28         {
29            对从index位置开始到len进行全排列;
30            位置index出有len-index+1 中取值情况分别是, indwx, indwx+1,....len-1;
31            for i = index --len-1
32            {
33                  使:index 取不同值:arr[index] = arr[i],
34                  对 index+1---len进行全排列
35                  回溯到起点:arr[index] = arr[i]
36            }
37         }
38         函数结束 return;
39 
40     */
41     if(index == len )
42     {
43         printf("%s\n",arr);
44     }
45     else
46     {
47         for(i = index; i < len ;i++)
48         {
49             swap(arr,index,i); //设index出值为arr[i]
50             fullPermutation(arr,len, index+1); // 对后面的元素全排列
51             swap(arr,index,i); //排列完后回溯的原点
52         }
53     }
54     return 0;
55 }
56 int main()
57 {
58     char  arr[] = "1234";
59     int len = sizeof(arr)/sizeof(char)-1;
60     fullPermutation(arr,len,0);        //从位置0出开始全排列
61     return 0;
62 }

2解:

     算法思想:

使用字典序输出全排列的思路是,首先输出字典序最小的排列,然后输出字典序次小的排列,……,最后输出字典序最大的排列这里就涉及到一个问题,对于一个已知排列,如何求出其字典序中的下一个排列这里给出算法。

  • 对于排列a[1...n],找到所有满足a[k]<a[k+1](0<k<n)的k的最大值,如果这样的k不存在,则说明当前排列已经是a的所有排列中字典序最大者,所有排列输出完毕
  • 在a[k+1...n]中,寻找满足这样条件的元素l,使得在所有a[l]>a[k]的元素中,a[l]取得最小值。也就是说a[l]>a[k],但是小于所有其他大于a[k]的元素
  • 交换a[l]与a[k].
  • 对于a[k+1...n],反转该区间内元素的顺序。也就是说a[k+1]与a[n]交换,a[k+2]与a[n-1]交换,……,这样就得到了a[1...n]在字典序中的下一个排列。
这里我们以排列a[1...8]=13876542为例,来解释一下上述算法。首先我们发现,1(38)76542,括号位置是第一处满足a[k]<a[k+1]的位置,此时k=2所以我们在a[3...8]的区间内寻找比a[2]=3大的最小元素,找到a[7]=4满足条件,交换a[2]和a[7]得到新排列14876532,对于此排列的3~8区间,反转该区间的元素,将a[3]-a[8],a[4]-a[7],a[5]-a[6]分别交换,就得到了13876542字典序的下一个元素14235678
View Code
 1 /**
 2     对位置start-end之间元素逆置
 3 */
 4 int reverse(char* arr,int start, int end)
 5 {
 6     int i;
 7     int len = start - end +1;
 8     if(len > 1)
 9     {
10         for(i = 0; i < len/2; i++)
11         {
12             swap(arr, start+i, end-i);     //交换元素;
13         }
14     }
15     return 0;
16 }
17 
18 int next_permutation(char* arr, int len)
19 {
20    //查找arr中 满足 arr[i] < arr[i+1]的最大i
21    int i, max_i = len -1;
22    int min_i;
23    for(i =0; i< len -1;i++)
24    {
25        if(arr[i] < arr[i+1]) max_i = i;
26    }
27    if(max_i == len -1) printf("%s is last string!\n",arr);
28    for(i = max_i+1, min_i = max_i +1; i<len ;i++)
29    {
30        if(arr[i] > arr[max_i] && arr[i] < arr[min_i]) min_i = i;
31    }
32    if(max_i < len )
33    {
34        swap(arr, max_i, min_i);
35        reverse(arr, max_i+1, len-1);
36    }
37    printf("%s\n",arr);
38    return 0;
39 }

  可以修改的地方:

View Code
 1  for(i =0; i< len -1;i++)
 2    {
 3        if(arr[i] < arr[i+1]) max_i = i;
 4    }
 5    if(max_i == len -1) printf("%s is last string!\n",arr);
 6 
 7 修改为:
 8 
 9     for(i = len -2; i >= 0 ; i--)
10     {
11         if(arr[i] < arr[i+1]) break; 
12     }
13     if(i == -1 ) printf("%s is last string \n",arr);

  :利用递增i找到最大i

3解:是对问题2的逆序 

1. 对于排列a[1..n],从尾端开始,找到第一个a[k]>a[k+1]并记录,若k<0,则说明a已经是字典序的最小者。
2. 在a[k+1..n]中,寻找小于a[k]的最大元素a[i],
3. 交换a[k]和a[i]的值,
4. 对于a[k+1..n],反转区间内元素的顺序。
这里我们还是以前面的例子来说明,初始时,a=14235678,首先找到1(42)35678,此时k=2,再找到比4小的最大数是3,此时a[i]=3, i=4, 交换a[i]和a[k]的值,得到a=13245678,最后反转a[k+1..n],得到最后的结果a=1387654
 
View Code
 1 int prev_permutation(char* arr, int len)
 2 {
 3     int max_i = 0, i;
 4     int min_i;
 5     for(i =0; i< len -1; i++)
 6     {
 7         if(arr[i] > arr[i+1]) max_i = i;
 8     }
 9     if(max_i == len -1) printf("%s is first string\n",arr);
10     for(i = max_i+1, min_i = max_i +1; i<len-1;i++)
11     {
12         if(arr[i] < arr[max_i] && arr[i]>arr[min_i]) min_i = i;
13     }
14     if(max_i < len)
15     {
16         swap(arr, max_i, min_i);
17         reverse(arr,max_i+1, len-1);
18     }
19     printf("%s\n",arr);
20     return 0;
21 }

 

主函数:
View Code
1 int main()
2 {
3     char  arr[] = "3124";
4     int len = sizeof(arr)/sizeof(char)-1;
5     //fullPermutation(arr,len,0);        //从位置0出开始全排列
6     prev_permutation(arr,len);
7     next_permutation(arr,len);
8     return 0;
9 }

补充:

       c++ STL中 next_permutation() 与prev_permutation()的使用;

View Code
 1 #include <iostream>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     int arr[] = {1,2,3,4};
 8     cout<<"next"<<endl;
 9     do
10     {
11         cout<<arr[0]<<arr[1]<<arr[2]<<arr[3]<<endl;
12     }while(next_permutation(arr,arr+4));
13     cout<<"prev"<<endl;
14     int arr2[] = {4,3,2,1};
15     do
16     {
17         cout<<arr2[0]<<arr2[1]<<arr2[2]<<arr2[3]<<endl;
18     }while(prev_permutation(arr2,arr2+4));
19     return 0;
20 }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/002blogs/archive/2013/03/19/2969042.html

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值