1. 程式人生 > >【LeetCode】31. Next Permutations 下一個全排列

【LeetCode】31. Next Permutations 下一個全排列

演算法小白,最近刷LeetCode。希望能夠結合自己的思考和別人優秀的程式碼,對題目和解法進行更加清晰詳細的解釋,供大家參考^_^

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

The replacement must be in-place, do not allocate extra memory.

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

題目讓按照字典序生成下一個字典序更大的全排列,如果字典序已經最大,則返回最小的字典序排列。

以之前的全排列問題不同的是(如46題),這一次不能使用遞迴的方法了。只能使用一種非遞迴的方法,據說這種方法14世紀的時候就已經提出來了,基本思路是這樣的(先來一段摘抄的英文),分為4步:

1. Find the largest index k such that nums[k] < nums[k + 1]. If no such index exists, the permutation is sorted in descending order, just reverse it to ascending order and we are done. For example, the next permutation of [3, 2, 1] is [1, 2, 3].
2. Find the largest index l greater than k such that nums[k] < nums[l].
3. Swap the value of nums[k] with that of nums[l].
4. Reverse the sequence from nums[k + 1] up to and including the final element nums[nums.size() - 1].

簡單翻譯一下:

1. 找到一個最大的索引k,使得nums[k]<nums[k+1],顯然,一旦找到這樣的k(k >= 0),說明nums[k+1]>nums[k+2]>nums[n](其中n+1為容器長度,這裡只考慮所有元素都不相同的情況),即這些元素是呈遞減排列的。如果找不到這樣的k,則說明所有元素都呈遞減排列,即字典序達到了最大,將其翻轉即可
2. 找到一個最大的索引l,使用nums[k]<nums[l]
3. 交換nums[k]和nums[l]
4. 翻轉nums[k]後面所有的元素,即翻轉nums[k+1]到nums[n]

單純理解起來可能有些困難,基本思路是把大數往前提,之後再將後面的元素字典序變最小。但按照上述思路寫程式還是十分簡單的:

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int len = nums.size();
        int k = -1, l = 0, i = 0;
        for (i = len - 2; i >= 0; --i) {
            if (nums[i] < nums[i+1]) {
                k = i; break;
            }
        }
        if (k == -1) {
            reverse(nums.begin(), nums.end()); // 已經是逆序,直接翻轉
            return ;
        }

        for (i = len - 1; i >= 0; --i) {
            if (nums[i] > nums[k]) {
                l = i; break;
            }
        }
        swap(nums[k], nums[l]); // 交換元素位置
        reverse(nums.begin() + k + 1, nums.end()); // 翻轉元素
    }
};

這種方法的一個強大之處在於,他已經包含了元素相等的情況,因此不需要再單獨考慮了。

有了上面的方法,46題的全排列問題也就迎刃而解了,先將元素排序,然後通過上述方法不斷生成下一個全排列,直到所有元素呈逆序排列,就說明我們已經找到了所有的全排列。