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

LeetCode 31. 下一个排列

文章目录

  • LeetCode 31. 下一个排列 - 最简算法实现
    • 🎯 核心思路
      • 📝 算法步骤(4步法)
    • 💻 最简短代码
    • 🎭 最易懂代码(注释版,推荐!!)
    • 🎬 可视化演示过程
      • 示例1:[1,2,3] → [1,3,2]
      • 示例2:[2,3,1] → [3,1,2]
      • 示例3:[3,2,1] → [1,2,3]
      • 复杂示例:[1,2,7,4,3,1] → [1,3,1,2,4,7]
    • 🎨 动画演示
      • 步骤动画图解
    • 📊 算法流程图
    • 🎨 数组变化可视化
      • 关键概念图解
    • 🧠 为什么这样做?
      • 核心洞察
      • 直观理解
    • 🔧 完整测试代码
    • ⚡ 性能分析
    • 🎯 关键记忆点
    • 📈 排列变化轨迹图
      • 以[1,2,3]的所有排列为例
    • 📋 数字操作追踪表(结合推荐的解法,仔细看这里,一定会懂的!!!)
      • 示例:[1,2,7,4,3,1] → [1,3,1,2,4,7]
      • 反转操作详解
    • 🎯 记忆技巧
      • 助记口诀
      • 三个关键位置

LeetCode 31. 下一个排列 - 最简算法实现

🎯 核心思路

找下一个排列就是找刚好比当前排列大一点点的排列。

📝 算法步骤(4步法)

  1. 从右往左找第一个小于右邻居的数(称为"突破点")
  2. 如果没找到,说明是最大排列,全部反转
  3. 如果找到了,从右往左找第一个大于突破点的数,交换它们
  4. 把突破点右边的部分反转

💻 最简短代码

class Solution {public void nextPermutation(int[] nums) {int i = nums.length - 2;// 步骤1:找突破点while (i >= 0 && nums[i] >= nums[i + 1]) {i--;}// 步骤2&3:如果找到突破点,找替换数并交换if (i >= 0) {int j = nums.length - 1;while (nums[j] <= nums[i]) {j--;}swap(nums, i, j);}// 步骤4:反转突破点右边部分reverse(nums, i + 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 start) {int left = start, right = nums.length - 1;while (left < right) {swap(nums, left++, right--);}}
}

总共仅20行核心代码!

🎭 最易懂代码(注释版,推荐!!)

强烈建议把该代码和下面可视化演示部分结合看

class Solution {public void nextPermutation(int[] nums) {int n = nums.length;int breakPoint = -1;for (int i = n - 2; i >= 0; i--) {//从n-2开始,不能从n-1开始,n-1的话,i+1=n-1+1=n,就越界了if (nums[i]<nums[i+1]) {breakPoint=i;break;}}if (breakPoint!=-1) {for(int i=n-1;i>breakPoint;--i){if (nums[i]>nums[breakPoint]) {swap(nums,i,breakPoint);break;}}}reverse(nums,breakPoint+1);}void reverse(int[] nums,int start){int left=start,right=nums.length-1;while (left < right) {swap(nums,left++,right--);}}void swap(int[] arr,int i,int j){int t = arr[i];arr[i]=arr[j];arr[j]=t;}
}

🎬 可视化演示过程

示例1:[1,2,3] → [1,3,2]

原数组: [1, 2, 3]↑  ↑  ↑0  1  2🔍 步骤1:从右往左找突破点
检查位置1: nums[1]=2 < nums[2]=3 ✓ 
找到突破点 i=1🔄 步骤3:从右往左找替换数
检查位置2: nums[2]=3 > nums[1]=2 ✓
找到替换数 j=2💱 交换 nums[1] 和 nums[2]
[1, 2, 3] → [1, 3, 2]🔄 步骤4:反转突破点右边部分(位置2开始)
右边只有一个数,无需反转✅ 最终结果: [1, 3, 2]

示例2:[2,3,1] → [3,1,2]

原数组: [2, 3, 1]↑  ↑  ↑0  1  2🔍 步骤1:从右往左找突破点
检查位置1: nums[1]=3 > nums[2]=1 ❌
检查位置0: nums[0]=2 < nums[1]=3 ✓
找到突破点 i=0🔄 步骤3:从右往左找替换数
检查位置2: nums[2]=1 < nums[0]=2 ❌
检查位置1: nums[1]=3 > nums[0]=2 ✓
找到替换数 j=1💱 交换 nums[0] 和 nums[1]
[2, 3, 1] → [3, 2, 1]🔄 步骤4:反转突破点右边部分(位置1开始)
反转 [2, 1] → [1, 2]
[3, 2, 1] → [3, 1, 2]✅ 最终结果: [3, 1, 2]

示例3:[3,2,1] → [1,2,3]

原数组: [3, 2, 1]↑  ↑  ↑0  1  2🔍 步骤1:从右往左找突破点
检查位置1: nums[1]=2 > nums[2]=1 ❌
检查位置0: nums[0]=3 > nums[1]=2 ❌
没有找到突破点 i=-1🔄 步骤4:反转整个数组(从位置0开始)
[3, 2, 1] → [1, 2, 3]✅ 最终结果: [1, 2, 3]

复杂示例:[1,2,7,4,3,1] → [1,3,1,2,4,7]

原数组: [1, 2, 7, 4, 3, 1]↑  ↑  ↑  ↑  ↑  ↑0  1  2  3  4  5🔍 步骤1:从右往左找突破点
检查位置4: nums[4]=3 > nums[5]=1 ❌
检查位置3: nums[3]=4 > nums[4]=3 ❌  
检查位置2: nums[2]=7 > nums[3]=4 ❌
检查位置1: nums[1]=2 < nums[2]=7 ✓
找到突破点 i=1当前状态: [1, 2, 7, 4, 3, 1]↑ 突破点🔄 步骤3:从右往左找替换数(找第一个>2的数)
检查位置5: nums[5]=1 < 2 ❌
检查位置4: nums[4]=3 > 2 ✓
找到替换数 j=4💱 交换 nums[1]=2 和 nums[4]=3
[1, 2, 7, 4, 3, 1] → [1, 3, 7, 4, 2, 1]当前状态: [1, 3, 7, 4, 2, 1]↑ 突破点右边需要反转🔄 步骤4:反转突破点右边部分(位置2开始)
反转 [7, 4, 2, 1] → [1, 2, 4, 7]
[1, 3, 7, 4, 2, 1] → [1, 3, 1, 2, 4, 7]✅ 最终结果: [1, 3, 1, 2, 4, 7]

🎨 动画演示

步骤动画图解

找突破点过程:
[1, 2, 7, 4, 3, 1]←←←←←从右往左检查每相邻对位置4-5: 3 > 1  ❌ 继续
位置3-4: 4 > 3  ❌ 继续  
位置2-3: 7 > 4  ❌ 继续
位置1-2: 2 < 7  ✅ 找到!突破点=1找替换数过程:
[1, 2, 7, 4, 3, 1]↑           ←←突破点        从右往左找>2的数位置5: 1 ≤ 2  ❌ 继续
位置4: 3 > 2   ✅ 找到!替换数=4交换过程:
[1, 2, 7, 4, 3, 1]↑        ↑交换这两个数
[1, 3, 7, 4, 2, 1]反转过程:
[1, 3, | 7, 4, 2, 1]反转右边部分
[1, 3, | 1, 2, 4, 7]最终结果:[1, 3, 1, 2, 4, 7]

📊 算法流程图

🎨 数组变化可视化

关键概念图解

🧠 为什么这样做?

核心洞察

  1. 为什么从右往左找?

    • 我们要的是"刚好大一点点"的排列
    • 改变越右边的数字,影响越小
  2. 为什么找"小于右邻居"的数?

    • 这是第一个可以"增大"的位置
    • 右边都是降序,无法再增大
  3. 为什么要反转右边部分?

    • 交换后,右边仍然是降序(最大排列)
    • 反转变成升序(最小排列),确保是"下一个"

直观理解

想象数字排列像一个"计数器":

[1, 2, 3]  计数器:123
[1, 3, 2]  计数器:132 (下一个)
[2, 1, 3]  计数器:213
...

我们的算法就是实现了这个"计数器+1"的操作!

🔧 完整测试代码

public class NextPermutationTest {public static void main(String[] args) {Solution solution = new Solution();// 测试用例int[][] testCases = {{1, 2, 3},      // → [1, 3, 2]{3, 2, 1},      // → [1, 2, 3]  {1, 1, 5},      // → [1, 5, 1]{2, 3, 1},      // → [3, 1, 2]{1, 2, 7, 4, 3, 1}  // → [1, 3, 1, 2, 4, 7]};for (int[] nums : testCases) {int[] original = nums.clone();solution.nextPermutation(nums);System.out.printf("输入: %s → 输出: %s%n",Arrays.toString(original),Arrays.toString(nums));}}
}

输出结果:

输入: [1, 2, 3] → 输出: [1, 3, 2]
输入: [3, 2, 1] → 输出: [1, 2, 3]
输入: [1, 1, 5] → 输出: [1, 5, 1]
输入: [2, 3, 1] → 输出: [3, 1, 2]
输入: [1, 2, 7, 4, 3, 1] → 输出: [1, 3, 1, 2, 4, 7]

⚡ 性能分析

  • 时间复杂度:O(n) - 最多扫描数组3遍
  • 空间复杂度:O(1) - 原地操作,只用几个变量

🎯 关键记忆点

  1. 四字口诀:找点、找数、交换、反转
  2. 两个查找:都是从右往左
  3. 一次反转:让右边变成最小排列

📈 排列变化轨迹图

以[1,2,3]的所有排列为例

📋 数字操作追踪表(结合推荐的解法,仔细看这里,一定会懂的!!!)

示例:[1,2,7,4,3,1] → [1,3,1,2,4,7]

步骤操作类型数组状态详情关键信息
初始原始状态
数值127431
索引012345
🎯 待处理的原始数组
1找突破点
数值127431
索引012345
标记突破点右邻居
🔍 nums[1]=2 < nums[2]=7
✅ i=1 为突破点
2找替换数
数值127431
索引012345
标记突破点替换数
🔍 从右往左找 > 2 的数
✅ nums[4]=3 > nums[1]=2
3交换操作
数值137421
索引012345
变化2→33→2
🔄 交换突破点与替换数
💫 位置1和位置4的值互换
4反转右边
数值131247
索引012345
说明反转区域:[7,4,2,1]→[1,2,4,7]
🔄 反转突破点右边部分
✅ 最终结果生成完毕

反转操作详解

反转步骤数组状态指针位置操作说明
反转前
数值137421
索引012345
指针leftright
left=2, right=5🎯 准备反转位置2-5的部分
第1步
数值131427
索引012345
指针leftright
left=3, right=4🔄 交换位置2和5:7↔1
第2步
数值131247
索引012345
状态left≥right
left=4, right=3🔄 交换位置3和4:4↔2
✅ 反转完成

🎯 记忆技巧

助记口诀

找点找数交换反转,
从右往左是关键,
突破点处做文章,
最小排列是终点。

三个关键位置

  1. 突破点:第一个可以"增大"的位置
  2. 替换数:刚好能让突破点增大的最小数
  3. 反转区:突破点右边需要变成最小排列

这个算法虽然只有20行代码,但蕴含了深刻的数学思想,是贪心算法的完美体现!

http://www.dtcms.com/a/363809.html

相关文章:

  • 机器人抓取中的力学相关概念解释
  • Crawl4AI:为LLM而生的下一代网页爬虫框架
  • 【机器学习入门】5.2 回归的起源——从身高遗传到线性模型的百年演变
  • 学习笔记 | 如何将MaxKB应用对外发布为MCP服务?
  • 嵌入式学习 51单片机基础
  • 数控机床相邻轨迹最大过渡速度计算方法介绍
  • 25 万/秒写入 + 70% 硬件节省,TDengine 在首自信工业时序数据平台中的落地
  • 别再误会了!Redis 6.0 的多线程,和你想象的完全不一样
  • 蒙特卡洛采样与粒子滤波算法学习
  • DP-观察者模式代码详解
  • 代码随想录笔记-回溯算法
  • AI 写作实战:用 GPT-4o+ Claude 3 生成小红书文案,转化率提升 30%
  • 一文看懂 FastDatasets:用 LLM 极速生成高质量 SFT 数据集(已支持 Hugging Face Spaces PyPI)
  • maven私有仓库配置
  • 犀牛派A1上使用Faster Whisper完成音频转文字
  • 【Medical Image Analysis 1区TOP】用于MRI重建的全局感受野傅里叶卷积块
  • 《LINUX系统编程》笔记p8
  • FPGA时序约束(四)--主时钟约束
  • ESLint 相关
  • 算法模板(Java版)_前缀和与差分
  • 2025大学生必考互联网行业证书排名​
  • Git 代码提交管理指南
  • 【鸿蒙面试题-6】LazyForEach 懒加载
  • 单多行文本溢出
  • 大数据毕业设计选题推荐-基于大数据的大学生就业因素数据分析系统-Spark-Hadoop-Bigdata
  • 从0到1掌握进度管理:核心概念解析+目标设定的新手友好指南!
  • Elasticsearch常用DSL快速查询指南
  • Linux一共有多少个版本?
  • 基于SpringBoot2+Vue2开发的储物柜管理系统
  • 详解STM32的完整启动流程