题目分析
题目链接:31. Next Permutation
这题让我们找到比输入数字排列恰好大一点点的数字排列。对这个问题的算法不仅适用于数字串,而且适用于任何有字典序的符号串。
为了方便讨论,我们将输入数字串称为串1,输出数字串称为串2。
输出的串2需要满足以下条件:
- 字典序增大。
字典序增大当且仅当:串2与串1从最高位的数开始一一对比,第一个不同的数字(假设是从左数第k位),必定是串2的第k位比串1的大。比如串1:214653与串2:215346,第一个不同的数字是从左数第三位,串2的第三位必须比串1的第三位大。 -
字典序的增大得尽可能少。
要满足以下3个子条件(优先满足1,然后满足2,最后满足3):- k尽可能地大。也就是说,串1和串2第一个不同的数字,尽可能来得晚一些。字典序越往右边权重越低,增加串1尽可能右边的数字。
- 串2的第k位数不但要比串1的大,而且要尽可能地小。因为第k位是第一个不同的数字,所以它的大小直接决定串2增加多少。
- 串2的第k位右边的部分,应该处于字典序最小的排列。因为第k位已经能够决定串2比串1大,所以第k位右边的那些数字排列不影响串1与串2之间的大小关系。
代码实现
class Solution
{
public:
void nextPermutation(vector<int> &nums)
{
int len = nums.size(), k = len - 2;
if (len <= 1)
return;
// 找出k
// 从右往左扫描,如果第k位数不是已经扫描到的数字中最大的,说明第k位数可以增大(只要将【已经扫描到的、比第k位大的数】与【第k位数】置换)
while (k >= 0 && nums[k] >= nums[k + 1])
{
--k;
}
if (k < 0)
{
// k < 0 说明输入串已经是递减的,无法再增大字典序
reverse(nums.begin(), nums.end());
return;
}
int bigger = k;
// 从第k位的右边找到一个【恰好比第k位大一点点】的数字,与第k位置换
while (bigger + 1 < len && nums[bigger + 1] > nums[k])
{
++bigger;
}
swap(nums[k], nums[bigger]);
// 此时第k位右边的数字串必定是递减的(最大字典序),我们只要逆转一下就变成递增的(最小字典序)
reverse(nums.begin() + k + 1, nums.end());
return;
}
};
算法需要做2次扫描(O(n)),再做一次数组逆转(O(n))。因此总的时间复杂度为O(n)。其中n为输入数字串的长度。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。