双指针题目:下一个排列

文章目录

题目

标题和出处

标题:下一个排列

出处:31. 下一个排列

难度

5 级

题目描述

要求

整数数组的一个排列是将其所有元素按序列或线性顺序排列。

  • 例如, arr = [1,2,3] \texttt{arr = [1,2,3]} arr = [1,2,3],以下是 arr \texttt{arr} arr 的全部排列: [1,2,3] \texttt{[1,2,3]} [1,2,3]、 [1,3,2] \texttt{[1,3,2]} [1,3,2]、 [2,1,3] \texttt{[2,1,3]} [2,1,3]、 [2,3,1] \texttt{[2,3,1]} [2,3,1]、 [3,1,2] \texttt{[3,1,2]} [3,1,2]、 [3,2,1] \texttt{[3,2,1]} [3,2,1]。

整数数组的下一个排列 是指其整数的下一个字典序更大的排列。更正式的表述是,如果数组的所有排列根据其字典序从小到大排列在一个容器中,那么数组的下一个排列是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

  • 例如, arr = [1,2,3] \texttt{arr = [1,2,3]} arr = [1,2,3] 的下一个排列是 [1,3,2] \texttt{[1,3,2]} [1,3,2]。
  • 类似地, arr = [2,3,1] \texttt{arr = [2,3,1]} arr = [2,3,1] 的下一个排列是 [3,1,2] \texttt{[3,1,2]} [3,1,2]。
  • 而 arr = [3,2,1] \texttt{arr = [3,2,1]} arr = [3,2,1] 的下一个排列是 [1,2,3] \texttt{[1,2,3]} [1,2,3],因为 [3,2,1] \texttt{[3,2,1]} [3,2,1] 不存在字典序更大的排列。

给定一个整数数组 nums \texttt{nums} nums,找出 nums \texttt{nums} nums 的下一个排列。

要求原地修改,只允许使用额外常数空间。

示例

示例 1:

输入: nums = [1,2,3] \texttt{nums = [1,2,3]} nums = [1,2,3]

输出: [1,3,2] \texttt{[1,3,2]} [1,3,2]

示例 2:

输入: nums = [3,2,1] \texttt{nums = [3,2,1]} nums = [3,2,1]

输出: [1,2,3] \texttt{[1,2,3]} [1,2,3]

示例 3:

输入: nums = [1,1,5] \texttt{nums = [1,1,5]} nums = [1,1,5]

输出: [1,5,1] \texttt{[1,5,1]} [1,5,1]

数据范围

  • 1 ≤ nums.length ≤ 100 \texttt{1} \le \texttt{nums.length} \le \texttt{100} 1≤nums.length≤100
  • 0 ≤ nums[i] ≤ 100 \texttt{0} \le \texttt{nums[i]} \le \texttt{100} 0≤nums[i]≤100

解法

思路和算法

如果数组 nums \textit{nums} nums 存在更大的排列,则一定存在两个下标 i i i 和 j j j 满足 i < j i < j i<j 且 nums [ i ] < nums [ j ] \textit{nums}[i] < \textit{nums}[j] nums[i]<nums[j]。为了找到下一个更大的排列,应取符合要求的最大的下标 i i i,在 nums [ i ] \textit{nums}[i] nums[i] 右侧寻找大于 nums [ i ] \textit{nums}[i] nums[i] 的最小元素并将该元素和 nums [ i ] \textit{nums}[i] nums[i] 交换,然后将 nums [ i ] \textit{nums}[i] nums[i] 右侧的全部元素按升序排序,即可得到下一个更大的排列。

如果数组 nums \textit{nums} nums 不存在更大的排列,则数组 nums \textit{nums} nums 单调递减,此时将整个数组反转得到单调递增的数组,即为下一个更大的排列。

具体做法如下。

  1. 反向遍历数组 nums \textit{nums} nums,找到符合 nums [ index 1 ] < nums [ index 1 + 1 ] \textit{nums}[\textit{index}_1] < \textit{nums}[\textit{index}_1 + 1] nums[index1]<nums[index1+1] 的最大下标 index 1 \textit{index}_1 index1。如果没有找到符合条件的下标 index 1 \textit{index}_1 index1,则 index 1 = − 1 \textit{index}_1 = -1 index1=−1。

  2. 如果 index 1 ≥ 0 \textit{index}_1 \ge 0 index1≥0,则找到符合 nums [ index 1 ] < nums [ index 2 ] \textit{nums}[\textit{index}_1] < \textit{nums}[\textit{index}_2] nums[index1]<nums[index2] 的最大下标 index 2 \textit{index}_2 index2,一定有 index 1 < index 2 \textit{index}_1 < \textit{index}_2 index1<index2,将 nums [ index 1 ] \textit{nums}[\textit{index}_1] nums[index1] 和 nums [ index 2 ] \textit{nums}[\textit{index}_2] nums[index2] 交换。如果 index 1 < 0 \textit{index}_1 < 0 index1<0 则不执行这一步。

  3. 将数组 nums \textit{nums} nums 从下标 index 1 + 1 \textit{index}_1 + 1 index1+1 到末尾的子数组反转(如果 index 1 = − 1 \textit{index}_1 = -1 index1=−1 则将整个数组反转),即可得到下一个排列。

证明

上述做法中,如果 index 1 < 0 \textit{index}_1 < 0 index1<0,则数组 nums \textit{nums} nums 中的每个元素都大于等于其后面的元素,因此数组 nums \textit{nums} nums 不存在更大的排列,下一个排列是将整个数组反转之后的结果。需要证明的是当 index 1 ≥ 0 \textit{index}_1 \ge 0 index1≥0 时上述做法得到的是下一个排列。

由于 index 1 \textit{index}_1 index1 是满足元素小于其后面的元素的最大下标,因此 nums [ index 1 ] \textit{nums}[\textit{index}_1] nums[index1] 右侧的元素均为单调递减,交换 nums [ index 1 ] \textit{nums}[\textit{index}_1] nums[index1] 右侧的元素不可能得到更大的排列。为了得到下一个更大的排列,必须在 nums [ index 1 ] \textit{nums}[\textit{index}_1] nums[index1] 右侧寻找大于 nums [ index 1 ] \textit{nums}[\textit{index}_1] nums[index1] 的最小元素,用 index 2 \textit{index}_2 index2 表示该最小元素所在下标,将 nums [ index 1 ] \textit{nums}[\textit{index}_1] nums[index1] 和 nums [ index 2 ] \textit{nums}[\textit{index}_2] nums[index2] 交换之后, nums [ index 1 ] \textit{nums}[\textit{index}_1] nums[index1] 替换成下一个更大的元素,符合下一个排列的规则。

在交换 nums [ index 1 ] \textit{nums}[\textit{index}_1] nums[index1] 和 nums [ index 2 ] \textit{nums}[\textit{index}_2] nums[index2] 之前, nums [ index 2 ] \textit{nums}[\textit{index}_2] nums[index2] 右侧的元素都小于等于 nums [ index 1 ] \textit{nums}[\textit{index}_1] nums[index1],下标范围 [ index 1 + 1 , index 2 − 1 ] [\textit{index}_1 + 1, \textit{index}_2 - 1] [index1+1,index2−1] 中的元素都大于 nums [ index 1 ] \textit{nums}[\textit{index}_1] nums[index1],且数组 nums \textit{nums} nums 从下标 index 1 + 1 \textit{index}_1 + 1 index1+1 到末尾的子数组单调递减,因此在交换 nums [ index 1 ] \textit{nums}[\textit{index}_1] nums[index1] 和 nums [ index 2 ] \textit{nums}[\textit{index}_2] nums[index2] 之后,数组 nums \textit{nums} nums 从下标 index 1 + 1 \textit{index}_1 + 1 index1+1 到末尾的子数组保持单调递减,将该子数组反转之后得到单调递增的子数组,符合下一个排列的规则。

因此上述做法得到的是下一个排列。

代码

java 复制代码
class Solution {
    public void nextPermutation(int[] nums) {
        int length = nums.length;
        int index1 = length - 2;
        while (index1 >= 0 && nums[index1] >= nums[index1 + 1]) {
            index1--;
        }
        if (index1 >= 0) {
            int index2 = length - 1;
            while (nums[index1] >= nums[index2]) {
                index2--;
            }
            swap(nums, index1, index2);
        }
        reverse(nums, index1 + 1);
    }

    public void swap(int[] nums, int index1, int index2) {
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }

    public void reverse(int[] nums, int start) {
        for (int i = start, j = nums.length - 1; i < j; i++, j--) {
            swap(nums, i, j);
        }
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。需要反向遍历数组 nums \textit{nums} nums 最多两次,然后使用双指针反转子数组。

  • 空间复杂度: O ( 1 ) O(1) O(1)。

相关推荐
小冻梨6661 天前
ABC444 C - Atcoder Riko题解
c++·算法·双指针
伟大的车尔尼3 天前
双指针题目:压缩字符串
双指针
hnjzsyjyj7 天前
洛谷 P13270:【模板】最小表示法 ← 双指针 + 解环成链
字符串·双指针·解环成链
燃于AC之乐14 天前
《算法实战笔记》第10期:六大算法实战——枚举、贪心、并查集、Kruskal、双指针、区间DP
算法·贪心算法·图论·双指针·区间dp·二进制枚举
睡不醒的kun16 天前
不定长滑动窗口-求子数组个数
数据结构·c++·算法·leetcode·职场和发展·双指针·滑动窗口
伟大的车尔尼17 天前
双指针题目:复写零
双指针
拾光Ծ18 天前
【优选算法】双指针算法:专题二
c++·算法·双指针·双指针算法·c++算法·笔试面试
艾莉丝努力练剑21 天前
【优选算法必刷100题】第007~008题(双指针算法):三数之和、四数之和问题求解
linux·算法·双指针·优选算法
老鼠只爱大米23 天前
LeetCode经典算法面试题 #24:两两交换链表中的节点(迭代法、递归法等多种实现方案详细解析)
算法·leetcode·链表·递归·双指针·迭代·链表交换