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

143 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情

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

一、题目描述:

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

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

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

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

分析

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

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

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

123
132
213
231
312
321

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

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

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

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

132
213

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

231

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

213

为什么需要翻转呢?

很明显 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 
}

四、总结:

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

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

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

今天就到这里,学习所得,若有偏差,还请斧正

欢迎点赞,关注,收藏

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

好了,本次就到这里

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

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