[NEO解题报告]《Leetcode》31 -- 下一个排列

1. 题目信息

1.1 题目描述

题目链接: 31. 下一个排列

实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。

1.2 测试用例

示例 1:

输入:nums = [1,2,3]
输出:[1,3,2]
  • 示例 2:
输入:nums = [3,2,1]
输出:[1,2,3]
  • 示例 3:
输入:nums = [1,1,5]
输出:[1,5,1]
  • 示例 4:
输入:nums = [5]
输出:[1]
  • 示例 5:
输入:nums = [1,2,3,5,4,2,1]
输出:[1,2,4,1,2,3,5]
  • 提示:
1 <= nums.length <= 100
0 <= nums[i] <= 100

2. 题目分析

2.1 手动实现next_permutation

分三步完成, 从右往左找出一个最小的可以交换的数字对进行交换, 使得序列变大一点点, 我们以[1,2,3,5,4,2,1]为例;

  • 先逆序找到第一个可用于和后面的值交换的较小值, 即找到第一个不满足升序的值, 数组中的 3 就是我们要找的这个 第一个较小值, 假设 下标 i, 此处例子中值为 2;
  • 再逆序找到这个倒着看升序. 顺着看是降序的序列 [5,4,2,1] 中的值大于nums[i]的第一个下标 j, 使 nums[i] < nums[j], 然后交换这两个数字, 此处例子就是找到 比3略大的4, 然后交换, 得到新数组: [1, 2, 4, 5, 3, 2, 1];
  • 对 排在 (i, end) 也就是 [i+1, end)中的数字进行升序排列, 因为刚刚的查找过程中得知该部分数据时降序的, 所以即直接逆序一下即可, 比如上面的 [5, 3, 2, 1]; => [1, 2, 3, 5], 整个数组 [1,2,3,5,4,2,1] 变为 [1,2,4,1,2,3,5];

2.2 标准库的参考实现

template<class BidirIt>
bool next_permutation(BidirIt first, BidirIt last)
{
    if (first == last) return false;
    BidirIt i = last;
    if (first == --i) return false;
 
    while (true) {
        BidirIt i1, i2;
 
        i1 = i;
        if (*--i < *i1) {
            i2 = last;
            while (!(*i < *--i2))
                ;
            std::iter_swap(i, i2);
            std::reverse(i1, last);
            return true;
        }
        if (i == first) {
            std::reverse(first, last);
            return false;
        }
    }
}

3. 代码详情

3.1 C++

3.1.1 手动实现next_permutation

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        if (next_permutation(nums.begin(), nums.end())) {
            return;
        }
        // 如果返回false, 说明已经达到最大的排列, 并且已经进行了逆转成最小序列;
        // cout << nums[0] << endl;
    }

    void nextPermutation_v1(vector<int>& nums) {
        // 执行用时:4 ms, 在所有 C++ 提交中击败了80.53%的用户
        // 内存消耗:11.8 MB, 在所有 C++ 提交中击败了49.02%的用户
        int i = nums.size() - 2;
        while (i >= 0 && nums[i] >= nums[i+1]) {
            i--;
        }

        // 找到从后往前的第一个下降值, 再次从后往前找到可以和这个值交换的第一个大于该下降值的值;
        if (i >= 0) {
            int j = nums.size() - 1;
            while (j >= 0 && nums[i] >= nums[j]) {
                j--;
            }
            // 一定可以找到这个合法的j;
            swap(nums[i], nums[j]);
        }
        reverse(nums.begin() + i + 1, nums.end());
    }
};

3.2 Python

  • 逆序找较小值, 找出略大值进行交换, 后面被交换区域内降序; 注意Python的原地修改写法;
class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        smaller_index = -1
        for i in range(len(nums) - 2, -1, -1):
            if nums[i] < nums[i + 1]:
                smaller_index = i
                break
        if smaller_index >= 0:
            for bigger_index in range(len(nums) - 1, -1, -1):
                if nums[smaller_index] < nums[bigger_index]:
                    nums[smaller_index], nums[bigger_index] = nums[bigger_index], nums[smaller_index]
                    break
            # print("after swap:", nums)
            nums[smaller_index+1:] = nums[smaller_index+1:][::-1]
            # print("after reverse:", nums)
            return
        
        # in dec order, just reverse
        nums[::] = nums[::-1]

4. 系列文章

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逸云沙鸥のIHave@Dream

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值