Chinaunix首页 | 论坛 | 博客
  • 博客访问: 180111
  • 博文数量: 43
  • 博客积分: 611
  • 博客等级: 中士
  • 技术积分: 1053
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-02 13:37
文章存档

2015年(3)

2013年(23)

2012年(17)

我的朋友

分类: C/C++

2013-01-23 20:10:37

       问题描述:以字典顺序产生所有排列。假定集合set是连续的并且按从小到大顺序排列好了的,并且有n个元素。

       思路:算法的思路分成两个部分:A是递归产生以某个数字开头的排列,B是调用A来依次生成  1为第一位的所有排列,2为第一位的所有排列,....n为第一位的所有排列。

       下面是A部分的详细思路:

        1.以1234为例子。从右到左来寻找< (j=i+1,i>0)。

        2.接着再从右到左查找第一个比大的元素,然后将二者交换位置,再将到集合末尾逆序。这样就找到了1234的按字典顺序的下一个元素。

        3.递归进行。要注意递归的一个关键就是递归的结束条件。这里,以1开头的排列的最后一个数是1432,也就是说最后一个元素到第2个元素(第一个元素为1,肯定不能动)都没有<,也就是说,第二位及以后的元素都按照逆序排列好了,也就是找到了最后一个元素。这就是递归结束的条件。

        A部分的代码如下:

//find out all of the permutation in the set with first element 1 to n.
 void find_next()
 {
     set_print();
     int index_i,index_j,index_k;
     flag=0;
     for(index_i=n-2;index_i>0;index_i--)       
         if(set[index_i]0;index_k--)
         if(set[index_k]>set[index_i])
             break;
     swap(&set[index_i],&set[index_k]);
     reverse(index_j,n-1);
     
     find_next();
 }

      下面是B部分的详细思路:        

       1.B部分的算法主要负责执行A算法之后的增大首位的转折点,举个例子,就是1432的下一个元素是2134,从首位是1变到了首位是2。

       2.当A算法执行完后,元素是1432,这时,将第4位与第1位交换,就会得到2431,那么,很容易看出除了第一位之外其余都是按照逆序排列的,2431肯定不是1432的下一个数,2431与1432之间肯定还存在有符合条件的排列。于是,将除了第一位之外的元素逆序,就可达到目的,即2134。这才是1432的下一个元素。

       3.当到达2431后,即2开头的最后一个元素,又将第3位与第一位交换,再将首位之后的元素逆序,即可得到3开头的第一个数。

       4.那么,可以总结出,当首位为1时,就将最后一位与首位交换;当首位为2时,就将倒数第2位与首位交换;当首位为3时,就将倒数第3位与首位交换.....依次类推。这其中的道理很容易想明白。这样持续n次,所有结果就都得到了。

       B部分的代码如下:

     int count=1;
     int i=n-1;
     while(i>=0)
     {
         set[0]=count++;
         find_next(); 
         swap(&set[0],&set[i]);
         reverse(1,n-1);
         i--;
     }

        完整的代码如下:

 

View Code 
 #include 
 #define MAX 1000
 
 int n=4; 
 int set[MAX]={1,2,3,4};
 int flag=0;
 
 //swap a and b
 void swap(int *a,int *b)
 {
     int temp=*a;
     *a=*b;
     *b=temp;
 }
 
 //reverse a range of a set
 void reverse(int start,int end)
 {
     int index_r=0;
     int new_end=end-start;
     for(index_r=0;index_r<=new_end/2;index_r++)
         swap(&set[index_r+start],&set[new_end-index_r+start]);
 
 }
 
 void set_print()
 {
     int index;
     for(index=0;index0;index_i--)       
         if(set[index_i]0;index_k--)
         if(set[index_k]>set[index_i])
             break;
     swap(&set[index_i],&set[index_k]);
     reverse(index_j,n-1);
     
     find_next();
 }
 
 int main()
 {
     int count=1;
     int i=n-1;
     while(i>=0)
     {
         set[0]=count++;
         find_next(); 
         swap(&set[0],&set[i]);
         reverse(1,n-1);
         i--;
     }
     return 0;
 }

      参考资料:《C语言名题精选百则技巧篇》

     如果你觉得我的文章对你有帮助,请赞一下,非常感谢!


阅读(1310) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~