把數列\((x_1,x_2,\cdots,x_n)\)變換順序爲\((x_{p(1)},x_{p(2)},\cdots,x_{p(n)})\),其中\(p\)是\(A=\{1,2,3,\cdots,n\}\)的一個排列,要求只使用\(O(1)\)的額外空間。例如,當數列爲\((10,20,30,40)\),\(p\)爲\((3,1,2,4)\)時獲得的數列是\((30,10,20,40)\).c++
對於映射\(p:A\rightarrow A\),它的含義是「排列後的數組每一個元素從哪裏來」。即:變換後,數組下標\(k\)處的數從變換前的下標\(p(k)\)處來。變換後下標爲\(p(k)\)處的數從變換前下標爲\(p(p(k))\)處的數來……所以咱們能夠把這條變換的鏈記爲:算法
每個下標都在惟一的一個「圈」內(原文這裏的用詞爲"cycle")。舉個例子:數組
對於給定的一個排列:函數
咱們能夠觀察到這樣的規律:spa
對於括號中的每一行。最後一個等式右側的數在\(p\)映射下的像,等於第一個等式左側\(p\)做用的原像。對於每個這樣的圈,咱們都能用大小爲\(O(1)\)的額外空間完成數字的交換。而這個交換咱們從每一個圈中的最小數開始作。code
for (int j = 1;j <= n;j++) { int k = p(j);//n while (k > j) {//n+a k = p (k);//a } if(k == j) { int y = x[j],l = p[k];//b while (l != j) {//b+c x[k] = x[l];//c k = l;//c l = p(k);//c } x[k] = y;//b } } /* 這裏b是變換p中"圈"的個數,c+b=n a的含義是:對於每一個j,在j所在的圈中第一個不大於j的數k距離p(j)的距離之和 */
最壞的狀況以下class
此爲\(a\)的最壞狀況和\(b\)的最好狀況。引用
此爲\(a\)的最好狀況和\(b\)的最壞狀況。im
對於上面引用的\(p\)的實例:di
把全部的圈按照下述規則排列
1.圈內最小的數排在第一
2.圈內最小的數較大的,排在前面
則以上的\(p\)將變爲\((5,6,9)(3,7)(2)(1,8,4)\),設\(q=(5,6,9,3,7,2,1,8,4)\).則這樣的\(p\)到\(q\)的變換構成了一個排列到排列的雙射。
這樣,咱們就能夠把求\(b\)的值轉化爲求\(\{1,2,\cdots,n\}\)中知足\(q(j)=\min\{q(i)|i\le i \le j \}\)的\(j\)的個數。使得知足這樣條件的\(j\)的個數爲\(k\)的\(n\)元排列數共有\(\left[\begin{array}{c}n\\k\end{array}\right]\)種。(第一類Stirling數)
因此:
(當\(n\)充分大時,\(O(1)\)的值收斂到歐拉常數)
其中
咱們定義以下函數
則
而
其中,\(r=j-i+1\)
由上式:
由上面計算的\(b\)的均值\(\overline b=\ln n +O(1)=O(\log n)\)
因此算法的平均時間複雜度是\(O(n\log n)\)