当前位置: 首页 > article >正文

算法随笔_6: 下一个排列

上一篇: 算法随笔_5:接雨水-CSDN博客

题目描述如下:

整数数组的一个 排列  就是将其所有成员以序列或线性顺序排列。

  • 例如,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 的下一个排列。

必须 原地 修改,只允许使用额外常数空间。

==============================

解题思路:

根据题目要求已知一个整数的排列,需要求出按字典序的下一个排列。也就是说,找到下一个比当前这个数更大的数,且这两个数要紧挨着。例如下面这些按字典序的排列:

12345

12354

12435

12453

12534

...

54321

首先,我们需要保证数值是增大的。

其次,要保证数值是逐渐增大的。

如何保证第一点呢?很明显,降序的排列肯定比升序的排列数值大。

如何保证第二点呢?我们一起来看一下下面的分析:

上面第一行"12345",45是升序,那好,我们交换一下它俩让其变为降序54就可以了。

第二行"12354",从右往左看,54本身就是降序,说明最后两位的字典序排列已经是最大数了,我们需要继续往左找,发现是35升序,从这儿开始,我们需要把后三位重新排列,并且要保证数值是逐渐增大。重新排列的原则就是从后面两位中找一个比3大的最小数,然后剩下的两位需要按升序排列。这样就变成了12435。这个例子可能不明显,我们换一个例子,比如: 12398,它的下一个就是12839。这里大家可能有个疑问,刚才我们提到降序能保证数值增大,这里为什么又按升序排列?因为我们既要保证数值增大,而且要保证数值是逐渐增大的。既然,需要变动的后三位的高位已经变大,那这个数值一定比以前的数值大。但要保证逐渐增大,所以剩下的低位按照升序排列。

到此时,算法的雏形已经基本完成,给定一个排列,我们就可以按照这个原则去重新排列,即可得到下一个大数。

剩下的问题我们就需要考虑算法的效率问题了。算法涉及三个阶段:

第一阶段,从后往前找到需要变动的最少位数。这个问题一次迭代就能搞定。从后往前的数字需要不断变大,爬升,当发现数字变小,下降时,即找到了需要变动的位数。我们可以用一个指针p来做这一步。

第二阶段,需要从p指针所指数字num后面的位数中找一个比num大的最小数。然后两者交换。

第三阶段,我们需要把剩余的位数做升序排列。因为此时num后面的位数仍然保持降序,所以两两首尾交换,即可。

整个算法,我们只使用了一个tmpNum的变量,来临时存储交换时的值,因此符合题目要求的“原地”修改数组,只使用额外常数空间。算法时间复杂度为O(N),空间复杂度为O(1)。

以下是Python版的代码:

class Solution:
    def swap(self, nums, i, j):
        tmpNum=nums[i]
        nums[i]=nums[j]
        nums[j]=tmpNum

    def nextPermutation(self, nums):
        num_len=len(nums)
        p1=0
        p2=0
        #第一阶段,从后往前找到需要变动的最少位数,p1指向降序的最高位,因此需要增大的是p1-1下标处
        for i in range(1, num_len):
            num2=nums[num_len-i]
            num1=nums[num_len-i-1]
            if num1<num2:
                p1=num_len-i
                break
        if p1>0:
            #如果p1>0, 第二阶段,需要从nums[p1-1]后面的位数中找一个比nums[p1-1]大的最小数
            #如果p1==0 表示整个数组就是降序排列,直接跳过此步骤
            for i in range(p1, num_len):
                if nums[p1-1]>=nums[i]:
                    p2=i
                    break
            self.swap(nums, p1-1, p2-1) #找到后交换位置
        #第三阶段,我们把剩余的位数,做升序排列。因为本身就是降序列,所以两两首尾交换,即可。
        cnt=int((num_len-p1)/2)
        for i in range(cnt):
            self.swap(nums, p1+i, num_len-i-1)
# 示例用法
solution = Solution()
#nums=[1,2,3,9,8,6,1]
#nums=[1,1,1]
#nums=[5,4,3,2,1]
nums=[1,5,1]
solution.nextPermutation(nums)
print(nums)


http://www.kler.cn/a/507859.html

相关文章:

  • 在Playwright中使用PO模式
  • [0242-07].第09节:SpringBoot中简单功能分析
  • 数仓建模:如何设计可扩展性较好的同环比计算模型?
  • Linux安装Docker教程(详解)
  • C# .NetCore 使用 Flurl.Http 与 HttpClient 请求处理流式响应
  • Java中如何实现对象的深拷贝和浅拷贝?
  • linux 安装PrometheusAlert配置钉钉告警
  • 博客搭建 — GitHub Pages 部署
  • Spark任务提交流程
  • 学习记录1
  • 免费送源码:Java+SpringBoot+MySQL SpringBoot网上宠物领养管理系统 计算机毕业设计原创定制
  • el-table中使用el-image图片预览被其他表格遮挡,使用z-index层级设置无效
  • 从 Web3 到元宇宙:探索数字身份的奇幻演变
  • Sqlmap入门
  • OpenCV-ED绘制的使用(附源码)
  • 1.17学习记录
  • 使用Docker部署postgresql
  • Navicat For Mysql 1112 导出密码破解 python
  • PHP生产管理系统
  • 算法-最大连续1的个数
  • IntelliJ IDEA 路径问题总结:如何配置并显示当前工作目录
  • Python学习之旅:入门阶段(七)数据结构
  • 【C++】反向迭代器
  • Kotlin语言的正则表达式
  • wordpress zibll 2025款新页脚-6ke论坛
  • uni-app:动态禁止下拉列表展示情况(如果下拉列表数据为空就拦截下拉框展示,显示提示信息)