【刷题日记】31. 下一个排列

简介: 【刷题日记】31. 下一个排列

本次刷题日记的第 102 篇,力扣题为:31. 下一个排列

一、题目描述:

继续来实实在在的来做 下一个排列的题目

image.png

二、这道题考察了什么思想?你的思路是什么?

题目需要我们做得事情比较明确,我们需要注意这几个点

  • 题目给出一个整数数组的一个排列,要求我们找到这个nums 的下一个排列
  • 什么叫下一个排列呢,指的是 nums 数组的全排列,按照顺序,正好比 nums 排列大的下一个排列
  • 题目要求我们只能在原数组 nums 上修改元素,只能使用常数级别的空间消耗
  • 且我们需要注意一点,如果当前的 nums 已经是最大的一个排列了,那么咱们就给他变成一个最小的排列

分析

首先,我们来看一下,什么是全排列

简单理解就是按照字典序,来将 nums 数组重新排列的各种排列方式,排列方式一般有 n*F(n-1) 种,其中 F(n-1) 表示 全排列 n-1 个元素的种类

例如,若题目 nums 示例是 [1, 2, 3] ,那么全排列就有 6 种,如下

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

那这个时候,很明确,如果我们 nums 是 [3, 2, 1] 的话,我们是找不到下一个更大的排列的,因此按照题意,需要我们直接对 nums 进行从下到大排序,咱们也可以直接去翻转 nums 也能得到 [1, 2, 3]

那么我们知道,要找下一个排列,实际上是在所有排列中正好比 nums 排列大的,我们仔细观察上面的全排列,上一行与下一行之间的区别

  • 我们会发现,上面一行的从右往左开始第一个小于右边挨着的数 x,总会和 从右往左开始第一个大于 x 的数字 y 进行交换,例如
1 2 3
1 3 2

我们会发现 2 和 3 交换之后,就得到从上一行到下一行的结果了,再如:

1 3 2
2 1 3

我们又发现 ,这个时候我们 从右往左开始第一个小于右边挨着的数 1,与 从右往左开始第一个大于 1 的数字 2 交换了,得到如下结果

2 3 1

然后 3 和 1 还进行了翻转,最终得到

2 1 3

为什么需要翻转呢?

很明显 x 的索引 比 y 的索引靠前,y 的数值又比 x 的数值大

因此 x 和 y 交换之后,数组排列本身已经变大了,x 后面的数据内容,很明显是挨个当前元素大于后面元素的(例如 31),这个时候,我们要尽可能的将这一段数据变小,得到的结果才会是下一个排列

至此,咱们做这个题的时候,实际上就是要知道如何去找到下一个排列的方法

  • 找到符合要求的 2 个数字的索引 i,j,将他们对应的数字交换
  • 将 i 后面的数组内容进行翻转即可

一起来愉快的手撸代码吧

三、编码

根据上述逻辑和分析,我们就可以翻译成如下代码

  • 从数组右往左遍历,找到第一个小于下一个元素的索引 i , 如果发现 i 小于 0 ,那么只会是当前的排列是最大的了,没有比当前排列还要大的数了,因此需要直接将整个 nums 翻转,变成最小的
  • 再从右往左遍历,找到第一个大于 索引i 对一个值的元素索引
  • 交换 i 和 j 索引对应的值
  • 将 i 后面的元素进行翻转

编码如下:

func nextPermutation(nums []int)  {
    // 从数组右往左遍历,找到第一个小于下一个元素的索引 i
    i := len(nums) - 2
    for i>=0 && nums[i] >= nums[i+1] {
        i--
    }
    if i < 0 {
        myReverse(nums[i+1:])
        return 
    }
    // 再从右往左遍历,找到第一个大于 索引i 对一个值的元素索引
    j := len(nums) - 1
    for j >=0 && nums[i] >= nums[j] {
        j--
    }
    // 交换 i 和 j 索引对应的值
    nums[i], nums[j] = nums[j], nums[i]
    // 将 i 后面的元素进行翻转
    myReverse(nums[i+1:])
    return
}
func myReverse(nums []int){
    n := len(nums)
    for i:=0; i<n/2; i++ {
        nums[i], nums[n-1-i] = nums[n-1-i], nums[i]
    }
    return 
}

四、总结:

image.png

本题的处理方式按照题目中的要求,只引入常数级别的空间消耗,因此空间复杂度是 O(1),

时间复杂度是O(n) ,此处的 n 是 nums 的长度,咱们这种处理方式的实际上顶多会遍历 2 次 nums,和 1次 翻转 nums 的某一段数据

原题地址:31. 下一个排列

欢迎点赞,关注,收藏

朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

image.png

好了,本次就到这里

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

我是阿兵云原生,欢迎点赞关注收藏,下次见~


相关文章
|
7月前
|
容器
《剑指offer》——刷题日记
《剑指offer》——刷题日记
|
Cloud Native
【刷题日记】1184. 公交站间的距离
好久不刷题,没有锻炼思维,感觉脑袋都要生锈了,刷题感觉还是要从简单题刷题才能慢慢找到感觉
|
Cloud Native
【刷题日记】88. 合并两个有序数组
本次刷题日记的第 34 篇,力扣题为:88. 合并两个有序数组 ,简单
|
算法 Cloud Native
【刷题日记】623. 在二叉树中增加一行
【刷题日记】623. 在二叉树中增加一行
【刷题日记】623. 在二叉树中增加一行
|
算法 Cloud Native
【刷题日记】1161. 最大层内元素和
【刷题日记】1161. 最大层内元素和
|
算法 测试技术 Cloud Native
【刷题日记】2104. 子数组范围和
对于很久没有刷题的我来说,突然开始也开始刷题了,不为别的,已经习惯参与掘金的活动了,同时也可以促进自己再回顾一下算法题,毕竟长时间不练,真的就生疏了
|
Cloud Native
【刷题日记】64. 最小路径和
本次刷题日记的第 39 篇,力扣题为:64. 最小路径和 ,中等
150 0
|
存储 算法 Cloud Native
【刷题日记】515. 在每个树行中找最大值
本次刷题日记的第 76 篇,力扣题为:515. 在每个树行中找最大值 ,中等
|
存储 索引 Cloud Native
【刷题日记】498. 对角线遍历
本次刷题日记的第 65 篇,力扣题为:498. 对角线遍历,中等
|
Cloud Native
【刷题日记】70. 爬楼梯
本次刷题日记的第 10 篇,力扣题为:70. 爬楼梯 ,简单