31.下一个排列
🧤 31.下一个排列
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
- 例如,
arr = [1,2,3]
,以下这些都可以视作arr
的排列:[1,2,3]
、[1,3,2]
、[3,1,2]
、[2,3,1]
。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
- 例如,
arr = [1,2,3]
的下一个排列是[1,3,2]
。 - 类似地,
arr = [2,3,1]
的下一个排列是[3,1,2]
。 - 而
arr = [3,2,1]
的下一个排列是[1,2,3]
,因为[3,2,1]
不存在一个字典序更大的排列。
给你一个整数数组 nums
,找出 nums
的下一个排列。
必须** 原地 **修改,只允许使用额外常数空间。
思路
基本思想
有点类似于贪心的思想,尽可能的修改给定数组的后面位置,这样才能找到下一个位置,修改的时候,需要找到一个合适的位置交换元素,这个合适的位置需要从后向前找,这样修改时修改的尽可能是低位,从而变化尽可能小,才能找到下一个排列
根据上面的分析,需要找到第一个可以交换的较小的数,所以第一步需要从后向前找到这个较小的数,所以需要从后向前找到第一个升序对,这个升序对的左边的元素就是可以替换的最小的数,对应到上图中就是[5,7]这个升序对,5就是可以替换的最小的数,将其替换就可以让整体变得稍微大一些
为了找到下一个排列,需要将这个最小的数替换为一个尽可能小的大数,也就是从后向前找出第一个比当前这个最小的数大的数,这个数是比这个最小的数大一点点的数,交换之后,可以让整体变大一点,对应到上图就是6,5和6交换之后,可以让整体变大,因为高位变大
交换之后,需要注意的是,高位变大,后面的序列需要变的最小,从而使其成为下一个排列,为了让后面的序列变得最小,将其升序排列就变得最小,对应到上图就是5变成6之后,高位变大,后面的[7,5,4]还是降序,将其变得最小就可以让其变成下一个排列
交换之后就形成了下一个排列,对应到下图中就是:
如果搜索下来没有发现一个升序对,那就是[3,2,1]的情况,根据题意,需要将字典重置为最小的排列,也就是将其升序排列即可,从[3,2,1]变成[1,2,3]直接逆序即可
执行流程
- 从后向前 查找第一个 相邻升序 的元素对 (i,j),满足
A[i] < A[j]
。此时 [j,end) 必然是降序 - .在
[j,end)
从后向前 查找第一个满足A[i] < A[k]
的 k。A[i]、A[k] 分别就是上文所说的「小数」、「大数」 - 将 A[i] 与 A[k] 交换
- 可以断定这时 [j,end) 必然是降序,逆置
[j,end)
,使其升序 - 如果在步骤 1 找不到符合的相邻元素对,说明当前
[begin,end)
为一个降序顺序,则直接跳到步骤 4
代码
根据以上分析,得出以下代码:
|
|
总结
类似于贪心的思想,尽可能找靠后的元素将其变大一点点,变大一点点之后,由于高位变大, 低位就可以变成最小,也不会影响整体变大,所以整体就是两步。第一步让尽可能靠后的高位变大,第二步让低位变得最小,经历两步之后就可以找到下一个排列