下一个排列
难度:
标签:
题目描述
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
- 例如,
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
代码结果
运行时间: 40 ms, 内存: 14.7 MB
/*
* The next permutation algorithm using Java Streams is generally not straightforward, but we can still use functional constructs:
* 1. Find the largest index k such that nums[k] < nums[k + 1]. If no such index exists, the permutation is the last permutation.
* 2. Find the largest index l greater than k such that nums[k] < nums[l].
* 3. Swap the value of nums[k] with that of nums[l].
* 4. Reverse the sequence from nums[k + 1] to the end.
*/
import java.util.stream.IntStream;
import java.util.Collections;
public class Solution {
public void nextPermutation(int[] nums) {
int k = IntStream.range(0, nums.length - 1)
.boxed()
.sorted(Collections.reverseOrder())
.filter(i -> nums[i] < nums[i + 1])
.findFirst()
.orElse(-1);
if (k == -1) {
reverse(nums, 0, nums.length - 1);
return;
}
int l = IntStream.range(k + 1, nums.length)
.boxed()
.sorted(Collections.reverseOrder())
.filter(i -> nums[i] > nums[k])
.findFirst()
.orElse(-1);
swap(nums, k, l);
reverse(nums, k + 1, nums.length - 1);
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
private void reverse(int[] nums, int i, int j) {
while (i < j) {
swap(nums, i, j);
i++;
j--;
}
}
}
解释
方法:
这个题解采用了两遍扫描的方法来找出下一个排列:第一次从后往前扫描找到第一个较小的元素,第二次从后往前找到第一个比较小元素大的元素,交换它们,然后反转后面的序列。具体步骤如下:
1. 从数组末尾开始,找到第一个比前一个元素小的元素 nums[i],表明从 i 开始的子序列是降序的。
2. 在 i 后面的降序子序列中,从后向前找到第一个比 nums[i] 大的元素 nums[k]。
3. 交换 nums[i] 和 nums[k]。
4. 反转 i 后面的子序列,使其变为升序。
这样就得到了下一个字典序更大的排列。
时间复杂度:
O(n)
空间复杂度:
O(1)
代码细节讲解
🦆
在从数组末尾开始寻找第一个比前一个元素小的元素时,如何确保这一步确实找到了正确的元素以构造下一个排列?
▷🦆
为什么选择交换找到的第一个较小的元素 nums[i] 和子序列中第一个比 nums[i] 大的元素 nums[k],这种交换的逻辑基础是什么?
▷🦆
在反转 nums[i] 后面的子序列以变为升序后,能否具体解释为何这一步确保生成的是字典序的下一个排列而非其他顺序?
▷🦆
如果 nums 数组已经是按照降序排序,这种方法如何确保将数组调整为字典序最小的排列?
▷相关问题
全排列
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入:nums = [0,1] 输出:[[0,1],[1,0]]
示例 3:
输入:nums = [1] 输出:[[1]]
提示:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums
中的所有整数 互不相同
全排列 II
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]]
示例 2:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
提示:
1 <= nums.length <= 8
-10 <= nums[i] <= 10