刷题学习记录——leetcode 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 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。

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

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

示例 3:
输入:nums = [1,1,5]
输出:[1,5,1]

初步思路

我们不需要面对整个数组的大调整,实际上字典更大的排列很多情况只需要针对数组末尾出现的第一对的满足前数小于后数的一对数,我们只需要交换
它们之间的位置就可以找到字典序更大的一个排列(只针对部分情况),这样的解法大概能通过一半的测试用例(未考虑边界条件)

但是这样的算法是有缺陷的,我们只能调整在末尾有满足我们交换需求的一对数的数组,如果对于[3,2,1]这样的数组中,压根就没有满足条件的一对数,我们没有考虑到他的处理,同时对于其他交换首尾位置的数组也无法处理,所以我们需要找到可以针对所有案例的方案。

class Solution {
    public void nextPermutation(int[] nums) {
        int len = nums.length;
        int flag = 0;
        for(int i = len; i > 1 ; i--)
        {
            if(nums[i-2] <= nums[i-1])
            {
                int temp = nums[i-1];
                nums[i-1] = nums[i-2];
                nums[i-2] = temp;
                flag = 1;
                break;
            }
        }

        if(flag == 0)
        {
            for(int i = 0;i < len;i++)
            {
                nums[i] = i + 1;
            }
        }


    }
}

看了题解之后

我们寻找一对满足条件的数只是找到了满足交换条件的较小的数,但是并不一定另一个数就是我们要交换的较大的那个数,所以我们用另一种方式来寻找这个要交换的较大的那个数。

观察之后我们可以知道,这个要交换的较大的数应该是靠近末尾且比较小的数大的数,于是我们对数组从末尾开始向前遍历,找到第一个比较小的数大的数然后让这俩数交换,但是此时仍未结束,因为他不符合线性顺序排列的题意,所以我们需要重排较小数右边的序列。

以[4,5,2,6,3,1]举例,第一个满足左<右的是2,6,于是较小的数确认为2,从末尾向前遍历,第一个比2大的数是3,于是较大的数确认为3,将他俩互换有:[4,5,3,6,2,1],但是显然这个不是下一个字典序更大的排列,我们需要对6,2,1三个数重排为1,2,6这样得到的[4,5,3,1,2,6]才是下一个字典序更大的排列(让后面的满足升序很重要,缺失则算法无法解决问题)

代码:

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;
            while(j >= 0 && nums[i] >= nums[j]){
                j--;
            }
            swap(nums,i,j);
        }
        //调整为升序
        reverse(nums, i + 1);
    }

    public void swap(int[] nums,int i,int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    //反转后半部分数组为升序
    public void reverse(int[] nums,int start){
        int left = start;
        int right = nums.length - 1;
        while(left < right){
            swap(nums,left,right);
            left++;
            right--;
        }
    }
}

题解中的解法比初步思路多的位置一个是较大数的确认,另一个是后半部分数组的升序排列,所以要多关注问题解决的完整性!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值