實現獲取下一個排列的函數,算法需要將給定數字序列重新排列成字典序中下一個更大的排列。
如果不存在下一個更大的排列,則將數字重新排列成最小的排列(即升序排列)。
必須原地修改,只允許使用額外常數空間。
以下是一些例子,輸入位於左側列,其相應輸出位於右側列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
題解:
1.找到最大索引k,以使a [k] <a [k + 1]。如果不存在這樣的索引,則該排列爲最後的排列,直接翻轉。
2.找出最大索引l,使a [k] <a [l]。由於k +1是這樣的索引,因此l定義明確,並且滿足k <l。
3.將a [k]與a [l]交換。
4.反轉從a [k +1]到最後一個元素a [n]的序列。
我可以解釋爲:
①我們首先從後往前尋找a [k] <a [k + 1]的位置,因爲我們假設索引 t > k, 那麼對於任意t,肯定存在 a[t] > a[t + 1] ,也就是在k後面的所以元素已經構成了最大字典序,我們無法再增大最大字典序了,只能對所以k位置的元素進行操作。
②現在我們找到了這樣一個位置後,我們要再找到一個位置與索引k進行交換才能生成下一個比原有排列字典序大且大的儘可能小的的排列假設我們從後往前找到了這樣一個索引l,使得a[l] > a[k],
③交換兩者之後,因爲存在a[l - 1] > a[l] > a[k] > a[l + 1],所以索引k之後的仍然是最大字典序,因爲我們要的是大的儘可能小的字典序,所以我們把索引k位置後的元素reverse一下變得到了最小的字典序。
eg: 2, 4, 3, 1
很明顯,2後面的元素已經構成了最大的字典序,僅對後面的三個元素進行操作時沒有用的了,因此按照前面說的第一步我們確定k = 0,a[k] = 2
現在我們從後往前找到大於a[k](也就是2)的第一個元素,明顯 l = 2, a[l] = 3
現在我們進行交換,3, 4, 2, 1 明顯索引0後的所以元素仍然構成最大字典序,按照題目要求我們reverse 索引k後面的所有元素 -> 得到 3, 1, 2, 4 就得到了我們要的結果
用我貧瘠的語言總結一下:我們從後往前尋找一個字典序已經最大的片段(也就是降序),因爲他字典序已經最大我們無法通過對這個片段進行操作獲得更大的字典序,因此我們對這個片段前一個索引位置進行交換操作(交換一個比它大且大的最小的元素),由於交換不改變那個片段仍然是最大字典序的事實,因此我們只需要在交換後reverse後面這個片段便可以得到我們想要的結果
參考代碼:
1 class Solution { 2 public: 3 void iter_swap(vector<int>::iterator a, vector<int>::iterator b) 4 { 5 int t = *a; *a = *b; *b = t; 6 } 7 void Next_permutation(vector<int>::iterator a, vector<int>::iterator b) 8 { 9 if(a==b) return ; 10 vector<int>::iterator i=a;++i; 11 if(i==b) return ; 12 i=b;--i; 13 while(true) 14 { 15 vector<int>::iterator j=i;--i; 16 if(*i<*(i+1)) 17 { 18 vector<int>::iterator k=b; 19 while(!(*i<*--k)) ; 20 iter_swap(i,k); 21 reverse(j,b); 22 break; 23 } 24 if(i==a) 25 { 26 reverse(a,b); 27 break; 28 } 29 } 30 return ; 31 } 32 void nextPermutation(vector<int>& nums) 33 { 34 Next_permutation(nums.begin(), nums.end()); 35 } 36 };