31. 下一个排列 js

解法一:

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var nextPermutation = function(nums) {
    
    let right = nums.length -1
    let left = right-1
    let flag = true  //用来控制循环的,只要数组发生了一次变化就立刻出循环
    let k = nums.length-1
    while(flag){
        if(left < 0){
            right = right-1 < 0 ? 0 : right-1  
            left = right -1 < 0 ? 0 :right-1 //left设置为right左边的数字,很容易变成-1,就跳到最后一个了,所以如果变成负的,就手动置零
        }else {
        if(nums[left] < nums[right]  ){  //判断相邻两个数是否有拐点出现
        for(; k > right ; k-- ){  //for循环找到除了拐点以外,比right的值要小的最大值,但是又不能比left的元素的值小
            if(nums[k] < nums[right] && nums[k] > nums[left])
            right = k
        }
           [nums[left],nums[right]] = [nums[right],nums[left]] // 这两个元素对换
           let arr = nums.splice(left+1,nums.length-left-1)
           arr.sort((a,b) => a-b) // left右边的值升序排列
           nums.push(...arr) // 数组合并
           flag = false
       }else{
           left -- 
           right -- 
       }}
       if(right == 0 && flag === true){ //数组到头了都没有拐点,说明是逆序排列的,直接数组反转
           nums.reverse()
           flag = false
       }
    }
    }

执行用时: 68 ms
内存消耗: 42.1 MB

主要思路:
  1. 要求输出的值要比上一次的大那么一点,但是只大一点点,所以就要在数字的尾部进行改动,让数组进行最小的增加,所以循环都是从尾部开始
  2. 要想进行最小的变化,就要把尾部的大的值往前和前面的小的值进行交换。
  3. 找到拐点,就是说,从后往前看,数字在变大,但有了一个比前一个小的值,算作拐点,例如:132 ,13就算做一个拐点,拐点左边是要交换到后面的小值(1),拐点右边是要放到前面的大的值(3),但是这样交换,不可以保证是最小的增加,例如312(13交换),但实际更小的增加是213(21交换)
  4. 所以在找到拐点之后,通过一个for循环从尾部开始找到,比拐点大的值小,但是又比拐点小的值大的数字,和拐点左边的值进行交换,所以有一个for循环,他从尾部开始循环,到right(right就是拐点右边的值的下标)的右边就结束。
  5. 找到之后,交换拐点左边的值(nums[left])和刚刚找到的偏小的大的值,可以在这里用解构赋值的语法交换
  6. 然后right右边的值升序排列,就会是最小值,所以把后面那一部分用splice提出来,排完序,再合回去。
  7. 如果一直循环到头都没有这个拐点,说明是降序排列,直接数组翻转

解法二(sort换成reverse):

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var nextPermutation = function(nums) {
    
    let right = nums.length -1
    let left = right-1
    let flag = true
    let k=nums.length-1
    while(flag){
        if(left < 0){
            right = right-1 < 0 ? 0 : right-1  
            left = right -1 < 0 ? 0 :right-1 
        }else {
        if(nums[left] < nums[right]  ){
        for(; k > right;k-- ){
            if(nums[k] > nums[left] && nums[k]<= nums[right])
            right = k
        }
           [nums[left],nums[right]] = [nums[right],nums[left]]
           let arr = nums.splice(left+1,nums.length-left-1)
           arr.reverse()
           nums.push(...arr)
           flag = false
       }else{
           left -- 
           right -- 
       }}
       if(right == 0 && flag === true){
           nums.reverse()
           flag = false
       }
    }
    }

执行用时: 84 ms
内存消耗: 41.7 MB

这里sort改成reverse也是一样的效果
首先,拐点右边的值一定是从左到右依次递增的,不然拐点就会出现
其次拐点左边的值和右边较小的大的值进行交换的时候,一定是和拐点左边的值差值最小的数,所以右边一定保持着原本的降序,不然右边选出来的值就不是这个了,因此用reverse翻转之后实际上就是升序后的结果
但是reverse花的时间更长
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值