31. 下一个排列

题目描述:

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

  • 例如,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 的下一个排列。必须 原地 修改,只允许使用额外常数空间。

 

解题思路:

示例:

arr = [1,2,3]的全部顺序排序为:
[1,2,3]
[1,3,2]
[2,1,3]
[2,3,1]
[3,1,2]
[3,2,1]

 

  我们应该从后往前遍历相邻两个数字,只要遇到正常升序的两数字,交换它们俩的位置必然能得到一个比原先排列大的排列(如:[1,3,2] -> [3,1,2])。但这并不能保证交换之后得到的是下一个排列,因为后面有比‘3’更小的数字‘2’,将它与‘1’交换得到的排列[2,3,1]比[3,1,2]更接近[1,3,2];但这也不是[1,3,2]的下一排列,因为[2,3,1]数字‘2’后面的序列如果能按升序顺序排列能得到更小的排序[2,1,3],这才是真正的下一排列。因此可以总结出解题步骤如下:

  1. 从后往前遍历,找到升序顺序的相邻两数字 nums[i] nums[i+1] 
  2. 重新从后往前遍历,找到比 nums[i] 大的数字 nums[j]  ,可以保证数字 nums[j] 是子排列 [nums[i+1],nums[i+2],...,nums[n-1]] 中的最小数字
  3. 交换 i 与 j 的位置;
  4. 由于交换之后的子排列 [nums[i+1],nums[i+2],...,nums[n-1]] 必然为非升序,因此只需要将子排列进行反转便可以得到升序的子排序,最后得到的排列就是下一排列。

算法正确性证明:

  以 [...,3,7,5,3,2,1] 这个排列说明一下,在第一步中,我们是以“找升序顺序的两数字”为目的从后往前遍历数组的。因此,在找到数字‘3’之前,遍历过的序列[7,5,3,2,1]必然“满足前一个数字大于或等于后一个数字”(也即非升序)。找到数字‘3’后,我们又在遍历过的序列[7,5,3,2,1]中找比‘3’大的数字‘5’,交换位置后的序列[7,3,3,2,1]必然也满足非升序顺序。因为 nums[i] >=nums[j+1]>=nums[j+2]>=... ,而nums[j-1]>=nums[j],因此交换nums[i]与nums[j]之后,由于nums[j]>nums[i],所以交换过后的子序列:...>nums[j-1]>nums[i]>=nums[j-1]>=...,满足非升序顺序。

 

代码实现:

复制代码
/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var nextPermutation = function(nums) {
    let i = nums.length-2; //对于长度为1的nums,直接跳过循环
    for(;i>=0;i--){
        if(nums[i]<nums[i+1]){
            for(let j=nums.length-1;j>=i+1;j--){
                if(nums[j]>nums[i]){
                    swap(nums,i,j);
                    break;
                }
            }
            reverse(nums,i+1);
            break;
        }
    }
    //如果整个nums为降序序列,那么它的下一排列就是升序序列,直接反转即可
    if(i<0){
        reverse(nums,0);
    }
};
function swap(nums,i,j){
    let temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}
function reverse(nums,start){
    let i = start;
    let j = nums.length-1;
    while(i<j){
        swap(nums,i,j);
        i++;
        j--;
    }   
}
复制代码

 

posted @   ˙鲨鱼辣椒ゝ  阅读(17)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示