1. 程式人生 > >演算法02:全排列字典序演算法

演算法02:全排列字典序演算法

(2)字典序法

對於數字1、2、3......n的排列,不同排列的先後關係是從左到右逐個比較對應的數字的先後來決定的。如{1,2,3}的排列中,132排在123之後,排在213之前。

演算法思路:

1.如對於{1,2,3,4,5}的一個排列p1為list[]={1,3,4,6,5,2},要求出下一個排列,首先要從最右邊開始,向左找到第一個左邊的數小於右邊的數的位置i,在p1中為46即i=2號位置(下標從0開始);

2.再從最右邊開始,找到比list[i]大的數中最小者的位置j,因為list中的數是按字典序排序的,所以list[i]以後的陣列元素是從大到小排列的,所以j即為從最右邊開始第一個比list[i]大的數的下標,p1為5中j=4;

3.交換list[i]、list[j]後list[]={1,3,5,6,4,2},此時5後面的數是從從大到小排列的,所以按字典序的下一個排列應該把後面的數倒轉,既得到下一個排列list[]={1,3,5,2,4,6}

完整演算法:

#include <cstdlib>
#include <iostream>

using namespace std;

int list[100], n;

//交換陣列兩個元素 
void swap(int i, int j)
{
     int temp;
    
     temp = list[i];
     list[i] = list[j];
     list[j] = temp;
}

//將陣列逆序 
void reverse_list(int i)
{
     int j, k;
     for(k = i+1, j = n-1; j > k; j--, k++)
             swap(j, k);
}

//輸出陣列 
void print_list()
{
     for(int i = 0; i < n; i++)
             cout<<list[i]<<" ";
         cout<<endl; 
}

//按字典序輸出全排列 
void next_perm()
{
      int i, j, total = 0;
      
      if(n == 1){
         print_list();
         return;
      }
      
      while(1){
            print_list();
            total++;
            
            //從最右邊開始,找第一個左邊小於右邊的數的位置 
            for(i = n-2; i >= 0; i--)
                if(list[i] < list[i+1]) break;
                
            // 沒有逆序對結束 
            if(i < 0) break;
          
            //從最右邊開始,找到第一個比list[i]小的數 
            for(j = n-1; j > i ;j--)
               if(list[j] > list[i]) break;
            
            swap(j, i);
               
            //將位置i後面的陣列元素逆序 
            reverse_list(i);
     }
     
     cout<<total<<endl;
}

int main()
{  
    while(cin>>n){
          for(int i = 0; i < n;)
             list[i] = ++i; 
    
          next_perm();    
    }
    
    system("PAUSE");
    return 0;
}