leetcode
leetcode 1 ~ 50
下一个排列

下一个排列

难度:

标签:

题目描述

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

  • 例如,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]时,它就标志着从i位置开始到数组结束的部分是降序的。这意味着,从i位置开始的子序列已经是该子序列能达到的最大排列,要找到整个数组的下一个排列,我们需要调整的就是这个位置。
🦆
为什么选择交换找到的第一个较小的元素 nums[i] 和子序列中第一个比 nums[i] 大的元素 nums[k],这种交换的逻辑基础是什么?
选择交换nums[i]和子序列中第一个比nums[i]大的元素nums[k]是为了确保得到的新排列尽可能小,且比当前排列大。通过这种交换,nums[i]位置被一个稍大的值替换,而这个值是从i位置之后可用的最小值中选择的,这保证了新排列在字典序上仅次于当前排列。
🦆
在反转 nums[i] 后面的子序列以变为升序后,能否具体解释为何这一步确保生成的是字典序的下一个排列而非其他顺序?
在交换nums[i]和nums[k]后,i位置之后的子序列仍然保持降序。这是因为在i之后的部分原来是降序的,交换两个元素并不会改变其他元素的顺序。反转i之后的子序列,将其变为升序,是为了得到从这一位置开始的最小可能排列。这样做保证了整体排列是所有大于原始排列的排列中最小的,即字典序的下一个排列。
🦆
如果 nums 数组已经是按照降序排序,这种方法如何确保将数组调整为字典序最小的排列?
如果数组nums是降序的,那么它已经是可能的最大排列。在这种情况下,第一次扫描将无法找到满足nums[i] < nums[j]的i,因为不存在这样的i。此时,整个数组将被反转变为升序,这是所有可能排列中字典序最小的排列。这样确保了算法能够从最大排列转到最小排列,完成一个循环。

相关问题

全排列

给定一个不含重复数字的数组 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

排列序列

给出集合 [1,2,3,...,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

给定 n 和 k,返回第 k 个排列。

 

示例 1:

输入:n = 3, k = 3
输出:"213"

示例 2:

输入:n = 4, k = 9
输出:"2314"

示例 3:

输入:n = 3, k = 1
输出:"123"

 

提示:

  • 1 <= n <= 9
  • 1 <= k <= n!

回文排列 II

回文排列 II