LeetCode 討論區中有一種解法看上去比較清晰,但是一開始不是太容易理解,分享一下自己的理解.
完成這道題分成3部分:
1)找到應該增大的位置i
2)將nums[i] 替換成一個比較大的數 nums[j]
3)將[i+1,末尾]的數都翻轉一次
感性的認識:next permutation 是要求 比當前這個數 稍微大一點點點的 那個數 ,那我們肯定是讓 後面的位 增大, 比如 123456 ,
肯定是讓123456 中的"5" 增大 , 這個5的位置是怎麼來的呢?
理性的推理:我們從上面的例子中可以推理出,我們要增大的位置 一定是 相對靠後的 , 那麼就可以 從後往前開始遍歷 ,
判斷 nums[i] < nums[i+1] 是否成立 , 如果成立的話 , 說明nums[i] 這個位上的數 就可以增大 . 如果不成立 , 那麼 i-- 即可
int i ; //i是當前考察的位置 , 與 i+1位置上的數進行比較
for(i = nums.size() ; i >= 0 ; i-- )
{
if(nums[i] < nums[i+1])
break;
}
這樣我們就記錄了 從後往前數 第一個可以被增大的位置 , 而且可以推斷出來的是 :範圍[i+1,nums.size()-1] 區間的 數 一定是單調遞減的,舉個例子: 123459876中 5是第一個從後往前找到的 非遞減的位置, 其後面的第6個位置到第9個位置都是單調遞減的.
值得注意的點是: i=-1的情況,意味着 整個序列都是單調遞減的,那麼我們就可以直接進入第3步 , 將987654321 翻轉成 123456789 當i >= 0 的情況 ,我們要進入第2步 現在我們完成了第1步
第2步中我們需要把nums[i] 替換成比它本身大的那個數 , 這個數 只能從[i+1,nums.size()-1]的範圍中尋找(因爲[0,i-1]範圍內的數是不能動的),又因爲[i+1,nums.size()-1]範圍內的數是單調遞減的 , 所以,我們可以從 後面開始尋找 第一個 大於 nums[i]的位置 j ,然後swap(nums[i] , nums[j]) ,交換後,[i+1,nums.size()-1]範圍的數 仍然是單調遞減的 .
舉兩個例子:123459876可以知道 5 和 6進行交換 123469875
12345987654321可以知道5也和6進行交換 , 12346987554321
這樣我們完成了第2步.
對i這個位置上的數進行增大後,我們要讓[i+1,nums.size()-1]範圍內的數 從小到大排列 , 而現在是 遞減的(非嚴格遞減) ,所以最後添加一句reverse(nums.begin()+i+1,nums.end()) ;
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int i ;
for(i = nums.size()-2 ; i >= 0 ;i--)
{
if(nums[i] < nums[i+1])
break;
}
if(i == -1)
{
reverse(nums.begin() , nums.end());
return ;
}
int j ;
for(j = nums.size() -1 ; j > i ; j-- )
{
if(nums[j] > nums[i])
break ;
}
swap(nums[i],nums[j]);
reverse(nums.begin() + i + 1 , nums.end());
return ;
}
};