LeetCode Hot100(63/100)——31. 下一个排列

hot100 31.下一个排列 由于3右边的数是递减的,所以从右向左找到的第一个大于3的数,就是3右边最小的大于3的数。由于只看前两位[1,4,_,_,_],就已经比[1,3,5,4,2]大了,所以后三位填最小的排列就行,即按照2,3,5的顺序填,得到[1,4,2,3,5]。由于第二步交换后,4右边的数5,3,2是递减的,所以只需要把5,3,2反转,就得到了答案[1,4,2,3,5]。(3)如果1保持不变,只重新排列3,5,4,2,这是可以的,因为3的右边有比3大的数,重排后可以让3这个位置上的数变大,从而得到更大的排列。 阅读详情

问题描述

实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

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

示例:

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

核心思想

要找到下一个排列,我们需要理解什么是"字典序中下一个更大的排列"。关键在于:我们希望增加的幅度尽可能小。

从右向左找第一个升序对

找到较小数nums[i]

从右向左找第一个大于nums[i]的数

交换这两个数

将i+1到末尾的序列反转

得到下一个排列

算法原理详解

观察规律

让我们通过一个例子来理解:[1, 5, 8, 4, 7, 6, 5, 3, 1]

原数组

1

5

8

4

7

6

5

3

1

关键观察:

  1. 从右向左看,[7, 6, 5, 3, 1] 是降序的,无法通过重排得到更大的数
  2. 继续向左,发现 4 < 7,这是第一个"较小数"
  3. 要让排列变大且增幅最小,需要用刚好大于4的数替换4
  4. 在右侧降序序列中,从右向左找第一个大于4的数,是5
  5. 交换4和5后,将右侧序列反转为升序

算法步骤可视化

步骤4: 反转 步骤3: 交换 步骤2: 找较大数 步骤1: 找较小数 步骤4: 反转 步骤3: 交换 步骤2: 找较大数 步骤1: 找较小数 [1,5,8,4,7,6,5,3,1] 找到i=3, nums[3]=4 找到j=6, nums[6]=5 [1,5,8,5,7,6,4,3,1] [1,5,8,5,1,3,4,6,7] 从右向左找第一个nums[i] < nums[i+1] 从右向左找第一个nums[j] > nums[i] 交换nums[i]和nums[j] 反转i+1到末尾的序列

为什么这样做是正确的?

下一个排列

目标

字典序更大

增幅最小

策略

尽量改动右侧

右侧影响小

找到突破点

第一个升序对

实现

交换

用刚好大的数替换

反转

保证右侧最小

核心逻辑:

  1. 从右向左找较小数:右侧降序部分已经是最大排列,无法调整,必须向左找到可以增大的位置
  2. 找刚好大于它的数:保证增幅最小
  3. 反转右侧:交换后右侧仍是降序,反转成升序使其最小

解法一:标准算法

代码实现

class Solution {
    public void nextPermutation(int[] nums) {
        int n = nums.length;
        
        // 步骤1: 从右向左找第一个升序对,找到较小数的位置i
        int i = n - 2;
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
        
        // 步骤2: 如果找到了较小数(不是完全降序)
        if (i >= 0) {
            // 从右向左找第一个大于nums[i]的数
            int j = n - 1;
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            // 交换
            swap(nums, i, j);
        }
        
        // 步骤3: 反转i+1到末尾的序列
        reverse(nums, i + 1, n - 1);
    }
    
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
    
    private void reverse(int[] nums, int left, int right) {
        while (left < right) {
            swap(nums, left, right);
            left++;
            right--;
        }
    }
}

复杂度分析

时间复杂度: O(n)

  • 找较小数:最坏O(n)
  • 找较大数:最坏O(n)
  • 反转操作:最坏O(n)
  • 总体:O(n)

空间复杂度: O(1)

  • 只使用了常数个额外变量
  • 原地修改数组

解法二:优化的查找方式

对于找较大数的步骤,由于右侧是降序的,我们可以使用二分查找优化。

代码实现

class Solution {
    public void nextPermutation(int[] nums) {
        int n = nums.length;
        
        // 找较小数
        int i = n - 2;
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
        
        if (i >= 0) {
            // 使用二分查找找较大数(右侧是降序的)
            int j = binarySearchGreater(nums, i + 1, n - 1, nums[i]);
            swap(nums, i, j);
        }
        
        // 反转
        reverse(nums, i + 1, n - 1);
    }
    
    // 在降序数组中二分查找第一个大于target的元素
    private int binarySearchGreater(int[] nums, int left, int right, int target) {
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > target) {
                left = mid + 1;  // 降序数组,大的在左边
            } else {
                right = mid;
            }
        }
        return left;
    }
    
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
    
    private void reverse(int[] nums, int left, int right) {
        while (left < right) {
            swap(nums, left, right);
            left++;
            right--;
        }
    }
}

复杂度分析

时间复杂度: O(n)

  • 虽然使用了二分查找O(log n),但整体仍是O(n)
  • 因为找较小数和反转操作都是O(n)

空间复杂度: O(1)

特殊情况处理

找到 i >= 0

未找到 i = -1

开始

是否找到较小数?

正常流程

完全降序

找较大数并交换

反转i+1到末尾

结束

反转整个数组

特殊情况: 当数组完全降序时(如[3,2,1]),找不到较小数,此时i=-1,直接反转整个数组得到升序排列。

算法执行示例

以 [1, 3, 5, 4, 2] 为例:

初始状态: [1, 3, 5, 4, 2]
                  ↑   ↑
                  i   降序开始

步骤1: 从右向左找,3 < 5,i = 1

步骤2: 从右向左找第一个大于3的数
       [1, 3, 5, 4, 2]
              ↑     
              j = 2 (nums[2]=5 > 3)

步骤3: 交换 nums[1] 和 nums[2]
       [1, 5, 3, 4, 2]

步骤4: 反转 i+1 到末尾
       [1, 5, 2, 4, 3]

结果: [1, 5, 2, 4, 3]

总结

下一个排列问题的核心是理解字典序和贪心思想:

  1. 从右向左找突破点:右侧降序部分已是最大,需要向左找可增大的位置
  2. 最小增幅原则:用刚好大一点的数替换,保证增幅最小
  3. 右侧最小化:交换后将右侧反转为升序,使整体尽可能小

这个算法巧妙地利用了排列的性质,通过O(n)时间和O(1)空间完成了任务,是一个经典的原地算法设计范例。

Reality Capture控制点修复/合并模型 通过增加控制点/修改对齐设置参数来修复模型被分为两个部分的解决方法 阅读详情

相关推荐

从零到一:基于STM32F103C8T6与HAL库的磁悬浮电赛装置实战解析

本文详细解析了基于STM32F103C8T6微控制器与HAL库,从零构建磁悬浮装置的完整实战方案。文章深入剖析了硬件设计中的线圈布局与“反向串联”巧思,以节省PWM资源;并阐述了采用三闭环PID控制算法的软件实现,结合Keil开发环境与HAL库进行工程搭建与调试。该方案为电子设计竞赛(电赛)提供了高可行性的参考,是嵌入式软硬件综合实践的优秀范例。

weixin_29169505的博客 273

Leetcode HOT 100 题目总结(已完结)

文章目录前引题目博客链接Easy No.1 - No.21 前引 这段时间感觉动态规划已经做伤了 就是真的没什么动力再做下去了 后面没做的也都是难的不行的dp题目了 想着还是要做的 这段时间换点口味 把力扣HOT 100题给全做了 写个总结 题目博客链接 Easy No.1 - No.21 461. 汉明距离 617. 合并二叉树 104. 二叉树的最大深度 206. 反转链表 169. 多数元素 21. 合并两个有序链表 ...

Love 6's Private Blog 1万+

泛微Ecology9单点登录实战指南:从Token获取到页面跳转

本文提供了一份详尽的泛微Ecology9单点登录实战指南,重点解析了Token跳转式SSO的核心流程。内容涵盖从理解关键参数(如appid、loginid)与一次性Token机制,到通过代码示例演示如何正确调用接口获取Token,并详细说明了拼接不同场景跳转地址的完整规则与安全实践,旨在帮助开发者高效、稳定地实现业务系统与OA门户的无缝集成。

ol789012345的博客 1064

(LeetCode-Hot100)31. 下一个排列

本文介绍了LeetCode第31题"下一个排列"的解法。题目要求在O(1)空间复杂度下找到给定排列的下一个字典序更大的排列。核心思路是:1)从右向左找第一个下降位置i;2)从右向左找第一个大于nums[i]的位置j;3)交换nums[i]和nums[j];4)反转i+1到末尾的子数组。算法时间复杂度O(n),空间复杂度O(1),满足题目要求。文章详细说明了解题步骤、代码实现(Java/Go)、示例演示和正确性证明,并总结了核心要点和面试技巧。

小飞的博客 760

c764-基于Vue的实时温湿度监控系统

🌟 毕业设计指导交流 🌟 同学们好!作为一名计算机专业的技术爱好者,我在毕业设计领域积累了一些经验,希望能和大家分享交流。从选题到答辩,欢迎一起探讨技术问题。💡 技术方向: 熟悉Java全栈(SSM/Spring Boot)、Python数据分析、微信小程序/安卓开发等技术栈,可以互相学习项目开发经验。📂 学习资源: 整理了一些开源项目案例(含代码和文档),需要参考的同学可以留言讨论,也欢迎分享你的项目经验。🛠 交流内容:✨ 欢迎在评论区留言讨论,一起进步!记得遵守社区规范,保持学术

weixin_39513710的博客 1069

【ESP32-S3量产指南】基于Flash下载工具实现Release模式Flash加密与安全配置

本文详细介绍了ESP32-S3量产阶段如何通过Flash下载工具实现Release模式下的Flash加密与安全配置。从加密模式选择、工具配置到分区表优化,提供了一套完整的量产安全解决方案,帮助开发者有效防止固件泄露和篡改,确保设备安全。

weixin_28730131的博客 230

力扣 hot100 -- 技巧

只出现一次的数字,多数元素,颜色分类,下一个排列,寻找重复数

IT OR 考公? 1644

《QGIS快速入门与应用基础》151:批量属性值赋值(字段计算器)

本文介绍了QGIS中字段计算器的批量属性值赋值方法,重点讲解了简单赋值(统一值和递增序列)的操作步骤与表达式应用。文章首先强调数据备份、字段类型确认等必要前提,随后详细解析字段计算器的界面功能分区和打开方式。通过具体场景示例,展示了如何为图层统一赋值固定值或生成连续编号序列,并提供了文本、数字、日期等不同字段类型的表达式模板。文中特别提醒新手优先创建新字段而非直接更新现有字段,以避免数据丢失风险,同时推荐使用$id和rownum()函数实现基础序列生成。该教程适合GIS初学者快速掌握批量属性编辑的核心技能。

LuckyHanMo的博客 486

力扣hot100

【代码】力扣hot100。

qq_59768361的博客 1400

sap资产post记录查询时候 报错 如图

SAP资产过账查询报错AA653解决方案摘要 错误原因:系统无法计算会计年度期间的最后一天,导致资产折旧/过账失败。常见原因包括:1)未创建2025年期间版本;2)期间日期映射不完整;3)依赖日历但未生成2025年日历。 解决方法: 通过OB37维护2025年12/13个完整期间 补充缺失期间或修正结束日期 使用SCAL生成2025年工厂日历 验证步骤: 检查T009R表2025年记录完整性 测试AFAB折旧运行 重新查询资产过账 提示:建议财务团队统一维护跨年度期间数据,并在测试系统验证变更。

金牌架构师 834

QT开发技术 [QXlsx 导出大量数据的崩溃大坑]

QXlsx 是一个基于 Qt 框架开发的开源库,用于读写 Excel 文件(.xlsx 格式),无需依赖 Microsoft Office 或第三方库。以下从多个方面为你详细介绍 QXlsx:主要特性读写功能:支持创建、读取和修改 Excel 文件,能处理单元格的数值、公式、格式等基本操作。样式设置:可以设置单元格的字体、颜色、对齐方式、边框等样式,还能创建自定义样式并应用到多个单元格。图表支持:支持创建多种类型的图表,如柱状图、折线图、饼图等,并能对图表的样式和属性进行配置。

weixin_45397344的博客 653

力扣HOT100之技巧:31. 下一个排列

下面再来讨论特殊情况,当原排列已经是最大的排列,不可能得到更大的排列时,我们需要将其转化为最小的排列,最大的排列一定是从头到尾都降序排列,我们将其逆转,得到升序排列,此时一定是最小的排列。我们先讨论最一般的情况:对于一组排列,我们要找到它的下一个排列,就需要从右往左遍历,找到第一个小于右边相邻元素的位置。到数组的倒数第二个元素,每一个元素都大于等于右边相邻的元素,从而形成降序排列。这个区间是降序排列的,我们仍需要对其进行逆转为升序排列使其变得更小,最终得到下一个排列,因此我们通过。),我们就需要找到元素。

weixin_52151595的博客 605

【SemiDrive源码分析】【X9芯片启动流程】11 - freertos_safetyos目录Cortex-R5 DIL2.bin 引导程序源代码分析

【SemiDrive源码分析】【X9芯片启动流程】11 - freertos_safetyos目录Cortex-R5 DIL2.bin 引导程序源代码分析

|~~~热爱生活、努力学习的小伙汁~~~| 2108

【Hot100】LeetCode—31. 下一个排列

31. 下一个排列。

weixin_44382896的博客 630

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

LeetCode-hot100题解—Day4

LeetCode-hot100题解-Day5

m0_49074706的博客 1002

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

力扣hot100:31. 下一个排列

我们会发现,我们只需要将右边较大的一个数与前面较小的一个数交换就能变得更大,不过为了更小我们需要将交换后的后面部分从小到大排序。我们考虑极端情况,如。,比它更大一点的将是。

m0_63997099的博客 741

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

leetcode HOT100总结

本人菜鸡一枚,历时两个月时间把HOT100题做完(Ctrl C-V)了,做完之后还是感觉到很虚,很多中等题还是不会做,于是乎花几天时间把这100题给总结一下。一方面,对自己的知识梳理;另一方面,便于后续回顾。(完成度99,是因为有一道题目收费,没有做!哈哈哈!)以下所有题目都是用Python3实现的。 下面是对这些题目按照算法进行分类整体,主要包括排序、哈希表、递归\回溯、链表、双指针、动态规划、二分查找、贪心、树、深度优先搜索、广度优先搜索等。 在整理之前,先学习一下Python的基本数据类型有哪些,请

qq_36561737的博客 4744

【NO.15】LeetCode HOT 100—31. 下一个排列

31. 下一个排列

wang_luwei的博客 1146

LeetCode 热题 HOT 100 题解(仅记录解题思路)

由于数组中只有一个元素出现一次,其余元素都出现两次,因此用集合中的元素之和的两倍减去数组中的元素之和,剩下的数就是数组中只出现一次的数字。遍历数组中的每个数字,如果集合中没有该数字,则将该数字加入集合,如果集合中已经有该数字,则将该数字从集合中删除,最后剩下的数字就是只出现一次的数字。指针p1在头结点处,指针p2在头结点的next处,然后p1每次移动1格,p2则走2步,若在其中一个进入null之前两者指向同一结点,则代表有环。使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。

a131529的博客 1万+

LeetCode HOT100

力扣HOT100刷题记录1.两数之和2. 两数相加3.无重复字符的最长子串6. Z字形变换7.整数反转 1.两数之和 这道题比较简单,直接利用哈希map建立一个字典,然后遍历循环,当前面存的key存在等于target-nums[i],则返回找到的数字的索引。若不存在,则将键值对放入hashmap中。最后遍历结束如果还没找到,就返回一个int数组[0],空数组。 class Solution { public int[] twoSum(int[] nums, int target) {

jiandandian_的博客 1717

LeetCode 热题 HOT 100 Java题解——31. 下一个排列

LeetCode 热题 HOT 100 Java题解31. 下一个排列数学推导复杂度分析 31. 下一个排列 题目: 实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。 必须原地修改,只允许使用额外常数空间。 示例: 以下是一些例子,输入位于左侧列,其相应输出位于右侧列。 1,2,3 → 1,3,2 3,2,1 → 1,2,3 1,1,5 → 1,5,1 数学推导 字典序下一个排列其实就是让当前的排

qq_20067165的博客 251

leetcode hot100特殊题型

136. 只出现一次的数字题解:涉及数字的要求线性时间的, 一般尝试能不能用位运算实现只出现一次的数字, 多个数字异或, 两个相同的数字异或为0, 那么最终剩下的就是只出现一次的数字 169. 多数元素题解:第一种, 使用哈希表存储每个元素出现的个数, 超过n/2的即为多数元素, 多数元素只可能有一个, 所以找到即可return第二种, 由于多数元素总是>n/2的, 因此排序后索引值n/2处必定是多数元素. 75. 颜色分类题解:只有三个元素, 那么遇到红色插入0后, 遇到蓝色插入2前即可 31

weixin_74191696的博客 1244

leetcode hot100【LeetCode 31.下一个排列】java实现

LeetCode 31.下一个排列

DataCrafter的博客 684

【Leetcode hot 100】31.下一个排列

该问题要求在给定整数数组中找到字典序中的下一个更大排列,若不存在则返回最小排列(升序排列)。关键步骤如下:1.从后往前找第一个小于其后元素的索引k;2.若不存在k则翻转整个数组;3.否则找到k后从后往前找第一个大于nums[k]的索引l,交换nums[k]和nums[l],再翻转k+1到末尾部分。解法时间复杂度O(n),空间复杂度O(1)。例如[1,2,3]的下一个排列是[1,3,2],而[3,2,1]的下一个排列是[1,2,3]。

专注Java和MySQL领域知识分享 449

最详细,最简单的力扣(leetcode)hot 100 技巧篇讲解

最详细的力扣(leetCode) hot 100 技巧篇章的教学。保证看了一遍就懂

yyyCode的博客 1546

力扣hot100——第5天:22括号生成、23合并K个升序链表、31下一个排列

所以这里需要一个死循环,然后在循环里面判断当前选择的节点,如果当前没有选择到有效节点,说明所有的链表都是空了,则说明所有的链表都遍历完毕了,可以跳出循环了。其实这道题目和昨天的合并两个升序链表思路基本是相同的,只不过由于有多个链表,所以代码中寻找当前位置的最小节点的时候需要一个for循环来判断所有的节点,从而寻找一个最小的节点。这道题目是使用递归的方法来求解,因为要求解所有的括号的可能情况,所以必须使用递归的方法来收集结果,而不能使用动态规划的方法。不同的当前节点决定了它后面展开的子树也是不同的。

qq_42731705的博客 690
上一篇: LeetCode Hot100(62/100)——62. 不同路径
下一篇: LeetCode Hot100(64/100)——70. 爬楼梯
TracyCoder123
博客等级 码龄8年 1万+粉丝 226原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TracyCoder123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值