力扣第三十一题——下一个排列

内容介绍

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

  • 例如,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]

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 100

完整代码

 void swap(int *a, int *b) {
    int t = *a;
    *a = *b, *b = t;
}
void reverse(int *nums, int left, int right) {
    while (left < right) {
        swap(nums + left, nums + right);
        left++, right--;
    }
}

void nextPermutation(int *nums, int numsSize) {
    int i = numsSize - 2;
    while (i >= 0 && nums[i] >= nums[i + 1]) {
        i--;
    }
    if (i >= 0) {
        int j = numsSize - 1;
        while (j >= 0 && nums[i] >= nums[j]) {
            j--;
        }
        swap(nums + i, nums + j);
    }
    reverse(nums, i + 1, numsSize - 1);
}

思路详解

  1. swap函数:这是一个辅助函数,用于交换两个整数的值。它接收两个整数的指针作为参数,通过临时变量t来实现两个整数的交换。

  2. reverse函数:这是一个辅助函数,用于反转数组中指定范围的元素。它接收数组的指针、左边界索引和右边界索引作为参数。通过头尾元素交换的方式,将数组指定范围内的元素进行反转。

  3. nextPermutation函数:这是主函数,用于生成给定数组的下一个排列。以下是详细步骤:

    a. 从后向前查找第一个相邻升序的元素对(即找到第一个nums[i] < nums[i + 1]的位置)。如果不存在这样的元素对,说明当前数组是最大的排列,直接反转整个数组即可得到最小的排列。

    b. 在步骤a找到的元素nums[i]之后,从后向前查找第一个大于nums[i]的元素nums[j]

    c. 交换nums[i]nums[j]的值。

    d. 反转nums[i + 1]nums[numsSize - 1]的元素,使得这部分元素按升序排列。

下面通过一个示例来具体说明这个过程:

假设数组为:1 2 3 4 3 2 1

步骤a:从后向前查找,找到nums[3] = 4nums[4] = 3,满足nums[i] < nums[i + 1],因此i = 3

步骤b:在nums[i + 1]nums[numsSize - 1]范围内,找到第一个大于nums[i]的元素,即nums[6] = 1

步骤c:交换nums[3]nums[6],数组变为:1 2 3 1 3 2 4

步骤d:反转nums[4]nums[6]的元素,数组变为:1 2 3 1 2 3 4

最终得到的数组1 2 3 1 2 3 4就是原数组1 2 3 4 3 2 1的下一个排列。

知识点精炼

一、指针与地址操作

  1. 指针:一种特殊变量,用于存储变量地址。
  2. 解引用:通过指针访问其所指向的变量,使用操作符 *

二、函数定义与调用

  1. 函数定义:使用 return_type function_name(parameters) 语法声明函数。
  2. 函数参数:通过指针传递地址,实现对原始数据的修改。

三、数组操作

  1. 数组元素访问:通过索引和指针运算访问数组元素。
  2. 数组区间操作:通过循环和指针操作实现对数组的区间操作。

四、算法逻辑

  1. 排列生成:通过交换和反转操作生成数组的下一个排列。
  2. 双指针技巧:使用两个指针分别从前后遍历数组,提高查找效率。

五、循环与条件判断

  1. while 循环:在不确定循环次数时,使用 while 循环实现条件控制。
  2. if-else 判断:根据条件执行不同的代码块。

六、代码优化

  1. 代码简洁性:通过逗号表达式和简洁的赋值语句减少代码行数。
  2. 减少不必要的操作:在循环和条件判断中,尽量减少不必要的计算和比较。

以下是具体知识点与代码的对应关系:

  • swap 函数:涉及指针解引用和临时变量交换。
  • reverse 函数:使用双指针技巧,通过头尾元素交换实现数组反转。
  • nextPermutation 函数:实现排列生成的算法逻辑,包括查找降序对、交换元素和反转数组区间。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值