全排列演算法的遞迴與非遞迴實現

2021-08-17 05:58:25 字數 3176 閱讀 1676

對於給定的集合a,其中的n個元素互不相同,如何輸出這n個元素的所有排列(全排列)。

例如:給定集合[1,2,3] 它的全排列子集為:

[ [1,2,3],

[1,3,2],

[2,1,3],

[2,3,1],

[3,1,2],

[3,2,1]

]

思路:

1.保持第乙個數不變,對後面的數進行全排列

2.將第乙個數換成其它數,對後面的數進行全排列

3.第乙個數所有情況遍歷遍歷完成,得到全排列

例如:【1,2,3,4】

對1開頭的所有排列,得到【1,2,3,4】【1,2,4,3】【1,3,2,4】【1,3,4,2】【1,4,2,3】【1,4,3,2】

對2開頭的所有排列….依次類推

public list> permute(int nums) 

if(nums.length==0)

helper(nums,permutation,set,result);

return result;

}public

void

helper(int nums,listpermutation,

setset,list> result)

for(int i=0;iif(permutation.contains(nums[i]))

continue;

permutation.add(nums[i]);

set.add(nums[i]);

helper(nums,permutation,set,result);

set.remove(nums[i]);

permutation.remove(permutation.size()-1);}}

注意:此方法對於有重複元素的集合無效

為什麼需要兩個集合來儲存資料?

1.list不適合隨機訪問,適合順序訪問,remove(index)

2.list適合隨機訪問,不適合遍歷,remove(value)

3.indexoutofbound***ception錯誤提醒我們:使用remove()的方法時,要先從大到小的位置移除。當然如果你知道具體的物件,直接移除remove(物件)更穩妥。

思路:

1.對初始佇列進行排序,找到所有排列中最小的乙個排列pmin。

2.找到剛剛好比pmin大比其它都小的排列p(min+1)。

3.迴圈執行第二步,直到找到乙個最大的排列,演算法結束。

如排列12345,這是所有排列中最小的乙個排列,剛好比12345大的排列是:12354。

演算法如下:

給定已知序列p = a1a2a3…..an

對p按字典排序,得到p的乙個最小排列pmin = a1a2a3….an ,滿足ai > a(i-1) (1 < i <= n)

從pmin開始,找到剛好比pmin大的乙個排列p(min+1),再找到剛好比p(min+1)大的乙個排列,如此重複。

1.從後向前(即從an->a1),找到第一對為公升序的相鄰元素,即ai < a(i+1)。

若找不到這樣的ai,說明已經找到最後乙個全排列,可以返回了。

2.從後向前,找到第乙個比ai大的數aj,交換ai和aj。

3.將排列中a(i+1)a(i+2)….an這個序列的數逆序倒置,即an…..a(i+2)a(i+1)。因為由前面第1、2可以得知,a(i+1)>=a(i+2)>=…..>=an,這為乙個公升序序列,應將該序列逆序倒置,所得到的新排列才剛剛好比上個排列大。

4.重複步驟1-3,直到返回。

這個演算法是c++ stl演算法next_permutation的思想。

(盜來的圖,哈哈哈)

//排序

insertionsort(nums);

listarray= new arraylist();

for (int t : nums)

list.add(array);

int i;

while((i=hasnext(nums))>0)

j++;

}swap(nums,i-1,k);

//反轉

reverse(nums,i,nums.length-1);

listarr= new arraylist();

for (int t : nums)

list.add(arr);

}return list;

}public

inthasnext(int nums)

}return

0; }

public

void

reverse(int nums,int i,int j)

public

void

insertionsort(int arr)

arr[preindex+1] = current;

}

}使用字典序輸出集合的全排列需要注意,因為字典序涉及兩個排列之間的比較,對於元素集合不方便比較的情況,可以將它們在陣列中的索引作為元素,按照字典序生成索引的全排列,然後按照索引輸出對應集合元素的排列,示例**使用的就是此方法。對於集合a,可以對其索引1234進行全排列生成。這麼做還有乙個好處,就是對於字典序全排列生成演算法,需要從字典序最小的排列開始才能夠生成集合的所有排列,如果原始集合a中的元素不是有序的情況,字典序法將無法得到所有的排列結果,需要對原集合排序之後再執行生成演算法,生成索引的全排列,避免了對原始集合的排序操作。

字典序演算法還有乙個優點,就是不受重複元素的影響。例如1224,交換中間的兩個2,實際上得到的還是同乙個排列,而字典序則是嚴格按照排列元素的大小關係來生成的。對於包含重複元素的輸入集合,需要先將相同的元素放在一起,以集合a為例,如果直接對其索引123456進行全排列,將不會得到想要的結果,這裡將重複的元素放到相鄰的位置,不同元素之間不一定有序,得到排列a』,然後將不同的元素,對應不同的索引值,生成索引排列122334,再執行全排列演算法,即可得到最終結果。

全排列 遞迴與非遞迴實現

全排列問題在公司筆試的時候非經常見,這裡介紹其遞迴與非遞迴實現。簡單地說 就是第乙個數分別以後面的數進行交換 e.g e a b c 則 prem e a.perm b,c b.perm a,c c.perm a,b 然後a.perm b,c ab.perm c ac.perm b abc acb....

全排列的遞迴與非遞迴實現

1 全排列 將n個不同元素按照不同的順序進行排列,一般要求所有的排列方式,或者滿足某些要求的排列方式,比如先後順序的限制 2 遞迴實現全排列 eg 對 a b c d 進行全排列,可以按照以下的步驟 1.a後面加上 b c d 的全排列 2.b後面加上 a c d 的全排列 3.c後面加上 b a ...

全排列的遞迴與非遞迴實現

問題 輸入乙個序列 元素無重複 輸出其全排列 一般採用經典的遞迴解法,後來想將其改造為非遞迴 思考很久後覺得並不好寫,手工模擬遞迴棧的行為容易出錯。然後上網搜尋了一下眾的非遞迴 發現很多人的非遞迴 是各種全新的求解演算法,而不是相同演算法的非遞迴實現,和我想要的不一樣。遞迴解法 假設輸入序列 0,1...