LeetCode之HOT100--031 下一个排列

187 阅读1分钟

这是我参与11月更文挑战的15天,活动详情查看:2021最后一次更文挑战」。

前言

一直都计划学习数据结构与基本算法,但是平时都看一阵停一阵。现在决心坚持下去,我准备从LeetCode的HOT100开始,每天完成1~2道习题,希望通过这种方式养成持续学习的习惯。因为我是做iOS开发的,主要是用Objective-C语言,最近也在学习Swift,所以本系列的题解都将使用swift语言完成,本文更新的是LeetCode中HOT100的第15题031 下一个排列。

题目

实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列(即,组合出下一个更大的整数)。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。
示例 1:

输入:nums = [1,2,3]
输出:[1,3,2]

示例 2:

输入:nums = [3,2,1]
输出:[1,2,3]

示例 3:

输入:nums = [1,1,5]
输出:[1,5,1]

示例 4:

输入:nums = [1]
输出:[1]

提示:

1 <= nums.length <= 100
0 <= nums[i] <= 100

分析

“下一个排列”的定义是:给定数字序列的字典序中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
我们可以将该问题形式化地描述为:给定若干个数字,将其组合为一个整数。如何将这些数字重新排列,以得到下一个更大的整数。如 123 下一个更大的数为 132。如果没有更大的整数,则输出最小的整数。
如何得到这样的排列顺序?这是本文的重点。我们可以这样来分析:\

  1. 我们希望下一个数比当前数大,这样才满足“下一个排列”的定义。因此只需要将后面的「大数」与前面的「小数」交换,就能得到一个更大的数。比如 123456,将 5 和 6 交换就能得到一个更大的数 123465。
  2. 我们还希望下一个数增加的幅度尽可能的小,这样才满足“下一个排列与当前排列紧邻“的要求。为了满足这个要求,我们需要:
    2.1 在尽可能靠右的低位进行交换,需要从后向前查找
    2.2 将一个 尽可能小的「大数」 与前面的「小数」交换。比如 123465,下一个排列应该把 5 和 4 交换而不是把 6 和 4 交换
    2.3 将「大数」换到前面后,需要将「大数」后面的所有数重置为升序,升序排列就是最小的排列。以 123465 为例:首先按照上一步,交换 5 和 4,得到 123564;然后需要将 5 之后的数重置为升序,得到 123546。显然 123546 比 123564 更小,123546 就是 123465 的下一个排列 以上就是求“下一个排列”的分析过程。
    所以本题的题解思路如下:
1. 从后向前查找第一个相邻升序的元素对 (i,i+1),满足 A[i] < A[i+1]。此时 [i+1,end) 必然是降序2. 在 [i+1,end) 从后向前查找第一个满足 A[i] < A[j] 的 j。A[i]A[j] 分别就是上文所说的「小数」、「大数」
3. 将 A[i]A[j] 交换
4. 可以断定这时 [i+1,end) 必然是降序,逆置 [i+1,end),使其升序5. 如果在步骤 1 找不到符合的相邻元素对,说明当前 [begin,end) 为一个降序顺序,则直接跳到步骤 4

题解

    class KLLC031 {
    func nextPermutation(_ nums: inout [Int]) { 
        //从后向前查找第一个相邻升序的元素
        var i = nums.count - 2 
        while i >= 0 && nums[i] >= nums[i+1] { 
            i-=1 
        } 
        //如果i<0则表示整个都是降序,也就是当前排列是最大排列,直接逆序即可
        if i >= 0 { 
            //如果存在相邻升序,则从后往前找到第一个大于 nums[i]的数进行交换
            var j = nums.count - 1 
            while j >= 0 && nums[i] >= nums[j] { 
                j -= 1 
            } 
            nums.swapAt(i, j) 
        }
        //i之后的排列逆序成最小排列
        nums[i+1..<nums.count].reverse() 
    } 
}