LeetCode 第31题:下一个排列

LeetCode 第31题:下一个排列

题目描述

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

例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。

整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

难度

中等

题目链接

点击在LeetCode中查看题目

示例

示例 1:

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

示例 2:

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

示例 3:

输入:nums = [1,1,5]
输出:[1,5,1]

提示

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 100

解题思路

方法:从右向左扫描

这是一道经典的排列问题,需要理解字典序的性质。

关键点:

  1. 从右向左找第一个升序对
  2. 从右向左找第一个大于nums[i]的数
  3. 交换并反转后续数字

具体步骤:

  1. 从右向左找第一个升序对(i,i+1):
    • 找到第一个nums[i] < nums[i+1]的位置
    • 这个位置就是需要改变的位置
  2. 从右向左找第一个大于nums[i]的数:
    • 在i右侧找第一个大于nums[i]的数
    • 与nums[i]交换
  3. 反转i+1后的所有数字:
    • 因为i+1后的数字一定是降序的
    • 反转后得到升序

时间复杂度:O(n)
空间复杂度:O(1)

图解思路

算法步骤分析表

步骤 操作 数组状态 说明
初始状态 - [1,2,3,6,5,4] 原始数组
查找降序位置 从右向左扫描 [1,2,3,6,5,4] 找到3,因为3<6
查找替换数字 从右向左扫描 [1,2,3,6,5,4] 找到4,它是大于3的最小数
交换位置 swap(3,4) [1,2,4,6,5,3] 交换3和4的位置
反转后续 reverse [1,2,4,3,5,6] 反转4之后的所有数字

状态转换表

原始排列 下一个排列 转换说明
[1,2,3] [1,3,2] 交换3和2
[3,2,1] [1,2,3] 整体反转
[1,1,5] [1,5,1] 交换5和1

代码实现

public class Solution {
    public void NextPermutation(int[] nums) {
        int i = nums.Length - 2;
  
        // 找到第一个升序对
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
  
        if (i >= 0) {
            int j = nums.Length - 1;
            // 找到第一个大于nums[i]的数
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            // 交换
            Swap(nums, i, j);
        }
  
        // 反转i+1后的数字
        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);
            left++;
            right--;
        }
    }
}

执行结果

  • 执行用时:128 ms
  • 内存消耗:42.5 MB

代码亮点

  1. 🎯 原地修改数组,不使用额外空间
  2. 💡 巧妙利用数组特性进行反转
  3. 🔍 高效的查找算法
  4. 🎨 清晰的代码结构和命名

常见错误分析

  1. 🚫 没有考虑数组为空或只有一个元素的情况
  2. 🚫 查找替换位置的逻辑错误
  3. 🚫 反转操作实现不正确
  4. 🚫 边界条件处理不当

解法对比

解法 时间复杂度 空间复杂度 优点 缺点
暴力生成所有排列 O(n!) O(n) 直观易懂 效率极低
标准算法 O(n) O(1) 高效稳定 实现略复杂
二分查找优化 O(nlogn) O(1) 查找更快 整体收益有限

相关题目

posted @   旧厂街小江  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示