内容介绍
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
- 例如,
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);
}
思路详解
-
swap函数:这是一个辅助函数,用于交换两个整数的值。它接收两个整数的指针作为参数,通过临时变量
t
来实现两个整数的交换。 -
reverse函数:这是一个辅助函数,用于反转数组中指定范围的元素。它接收数组的指针、左边界索引和右边界索引作为参数。通过头尾元素交换的方式,将数组指定范围内的元素进行反转。
-
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] = 4
和nums[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
的下一个排列。
知识点精炼
一、指针与地址操作
- 指针:一种特殊变量,用于存储变量地址。
- 解引用:通过指针访问其所指向的变量,使用操作符
*
。
二、函数定义与调用
- 函数定义:使用
return_type function_name(parameters)
语法声明函数。 - 函数参数:通过指针传递地址,实现对原始数据的修改。
三、数组操作
- 数组元素访问:通过索引和指针运算访问数组元素。
- 数组区间操作:通过循环和指针操作实现对数组的区间操作。
四、算法逻辑
- 排列生成:通过交换和反转操作生成数组的下一个排列。
- 双指针技巧:使用两个指针分别从前后遍历数组,提高查找效率。
五、循环与条件判断
- while 循环:在不确定循环次数时,使用 while 循环实现条件控制。
- if-else 判断:根据条件执行不同的代码块。
六、代码优化
- 代码简洁性:通过逗号表达式和简洁的赋值语句减少代码行数。
- 减少不必要的操作:在循环和条件判断中,尽量减少不必要的计算和比较。
以下是具体知识点与代码的对应关系:
swap
函数:涉及指针解引用和临时变量交换。reverse
函数:使用双指针技巧,通过头尾元素交换实现数组反转。nextPermutation
函数:实现排列生成的算法逻辑,包括查找降序对、交换元素和反转数组区间。