全排列算法實現
1.全排列的定義和公式:
從n個數中選取m(m<=n)個數按照一定的順序進行排成一個列,叫作從n個元素中取m個元素的一個排列。由排列的定義,顯然不同的順序是一個不同的排列。從n個元素中取m個元素的所有排列的個數,稱為排列數。從n個元素取出n個元素的一個排列,稱為一個全排列。全排列的排列數公式為n!,通過乘法原理可以得到。
2.時間復雜度:
n個數(字符、對象)的全排列一共有n!種,所以全排列算法至少時間O(n!
3.列出全排列的初始思想:
解決一個算法問題,我比較習慣於從基本的想法做起,我們先回顧一下我們自己是如何寫一組數的全排列的:1,3,5,9(為了方便,下面我都用數進行全排列而不是字符)。
【1,3,5,9】(第一個)
首先保持第一個不變,對【3,5,9】進行全排列。
同樣地,我們先保持3不變,對【5,9】進行全排列。
保持5不變,對9對進行全排列,由於9只有一個,它的排列只有一種:9。
接下來5不能以5打頭了,5,9相互交換,得到
【1,3,9,5】
此時5,9的情況都寫完了,不能以3打頭了,得到
1,5,3,9
1,5,9,3
1,9,3,5
1,9,5,3
- 1
- 2
- 3
- 4
這樣,我們就得到了1開頭的所有排列,這是我們一般的排列數生成的過程。再接著是以3、5、9打頭,得到全排列。
我們現在做這樣的一個假設,假設給定的一些序列中第一位都不相同,那麽就可以認定說這些序列一定不是同一個序列,這是一個很顯然的問題。有了上面的這一條結論,我們就可以同理得到如果在第一位相同,可是第二位不同,那麽在這些序列中也一定都不是同一個序列。
T=【T=【 x1,x1, x2,x3,x4,x5,........xn−1,xn】x2,x3,x4,x5,........xn−1,xn】
我們獲得了在第一個位置上的所有情況之後(註:是所有的情況),對每一種情況,抽去序列TT 中的第一個位置,那麽對於剩下的序列可以看成是一個全新的序列
T1=【x2,x3,x4,x5,........xn−1,xn】T1=【x2,x3,x4,x5,........xn−1,xn】
序列T1T1 可以認為是與之前的序列毫無關聯了。同樣的,我們可以對這個T1T1 進行與TT 相同的操作,直到TT 中只一個元素為止。這樣我們就獲得了所有的可能性。所以很顯然,這是一個遞歸算法。
第一位的所有情況:無非是將x1x1 與後面的所有數x2,x3,.......xnx2,x3,.......xn 依次都交換一次
示意圖如下:
4.全排列的非去重遞歸算法
算法思路:全排列可以看做固定前i位,對第i+1位之後的再進行全排列,比如固定第一位,後面跟著n-1位的全排列。那麽解決n-1位元素的全排列就能解決n位元素的全排列了,這樣的設計很容易就能用遞歸實現。
【附代碼段:】
#include<iostream>
using namespace std;
int arr[5]={0,1,2,3,4};
void swap(int x,int y)//用於交換數組的兩個數
{
int temp=arr[x];
arr[x]=arr[y];
arr[y]=temp;
}
int resove(int n)//遞歸函數
{
if(n==5)//當嘗試對不存在的數組元素進行遞歸時,標明所有數已經排列完成,輸出。
{
for(int i=0;i<5;i++)
cout<<arr[i];
cout<<endl;
return 0;
}
for(int i=n;i<5;i++)//循環實現交換和之後的全排列
{//i是從n開始 i=n;swap(n,i)相當於固定當前位置,在進行下一位的排列。
swap(n,i);
resove(n+1);
swap(n,i);
}
}
int main()
{
resove(0);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
排列模板
void permutation1(char* str,int sbegin,int send) //全排列的非去重遞歸算法
{
if( sbegin == send) //當 sbegin = send時輸出
{
for(int i = 0;i <= send; i++) //輸出一個排列
cout << str[i];
cout << endl;
}
else
{
for(int i = sbegin; i <= send; i++) //循環實現交換和sbegin + 1之後的全排列
{
swap(str[i],str[sbegin]); //把第i個和第sbegin進行交換
permutation1(str,sbegin + 1,send);
swap(str[i],str[sbegin]); //【註1】交換回來
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
【註1】swap(str[i],str[sbegin])//交換回來
我們來仔細推敲一下循環體裏的代碼,當我們對序列進行交換之後,就將交換後的序列除去第一個元素放入到下一次遞歸中去了,遞歸完成了再進行下一次循環。這是某一次循環程序所做的工作,這裏有一個問題,那就是在進入到下一次循環時,序列是被改變了。可是,如果我們要假定第一位的所有可能性的話,那麽,就必須是在建立在這些序列的初始狀態一致的情況下,所以每次交換後,要還原,確保初始狀態一致。
全排列算法實現