算法题--下一个排列

106

leetcode原题:

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

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

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

题目读了几遍才理解,简单的说就是把数组元素拿来排序组成的数,然后返回比上一个数大的排列,以[1,2,3]为例,那元素1、2、3可以组成的数有:123、132、321、312、213、231,然后排序后: 123 < 132 < 213 < 231 < 312 < 321 ; 所以 [1,2,3] 后面的排列为 [1,3,2], [1,3,2]后面的排列为[2,1,3]....

暴力法肯定是不考虑的,由于题干里有说明:必须原地修改,只允许使用额外常数空间,给了一点思路,然后我就觉得这题很简单,开始一直错错错,最后才做出来。。

image.png

思路就是,现在从倒数第二个元素向左遍历,找到一个元素比他的右邻数要更小的,然后记录下位置i,然后再从数组的最后一个元素开始找,找到一个元素小于下标为i的元素,将这两个元素交换,换完之后我们已经保证现在的数一定是比之前的要更大的,但是在保证更大的情况下是最小的更大的数,以3 5 1 4 2为例,根据我们前面的思路,我们会交换12 的位置,那么现在排列就是 3 5 2 4 1,确实是比之前的数更大但是中介还有 3 5 2 1 4, 所以交换完位置后我们需要对2后面的数字做一个升序排序;

还有一种情况就是目前的排列已经是最大的数了,那排列一定是降序的,所以我们只需要将数组翻转就可以得到最小的排列;


/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var nextPermutation = function(nums) {
    let i = nums.length-2   // i的位置从数组的倒数第二个元素开始
    for (; i>=0; i--){
        if(nums[i] < nums[i+1]) {    // 找到比右邻数小的元素的下标i
            break
        }
    }
    let j=nums.length-1     // 从数组的末端开始
    while(j>0&&nums[j]<=nums[i]) j--     // 找到比下标i的元素更大的
    if(i>=0) {             // i>=0 就是存在小于右邻数的元素
        [nums[i],nums[j]] = [nums[j],nums[i]]  // 那就交换两个元素的位置
    }
    // 然后对数组 i+1 后面的元素进行升序排序
    let start = i+1;
    let end = nums.length - 1 
    while(start<end){
        [nums[start],nums[end]] = [nums[end],nums[start]]
        end--
        start++
    }
    return nums
};