贪心题目:交换一次的先前排列

文章目录

题目

标题和出处

标题:交换一次的先前排列

出处:1053. 交换一次的先前排列

难度

5 级

题目描述

要求

给定一个正整数数组 arr \texttt{arr} arr(其中的元素可能重复),返回可在一次交换 (交换两数字 arri \texttt{arri} arri 和 arrj \texttt{arrj} arrj 的位置)后得到的按字典序排列小于 arr \texttt{arr} arr 的最大可能排列。如果无法得到字典序更小的排列,返回原数组。

示例

示例 1:

输入: arr = 3,2,1 \texttt{arr = 3,2,1} arr = 3,2,1

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

解释:交换 2 \texttt{2} 2 和 1 \texttt{1} 1。

示例 2:

输入: arr = 1,1,5 \texttt{arr = 1,1,5} arr = 1,1,5

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

解释:已经是最小排列。

示例 3:

输入: arr = 1,9,4,6,7 \texttt{arr = 1,9,4,6,7} arr = 1,9,4,6,7

输出: 1,7,4,6,9 \texttt{1,7,4,6,9} 1,7,4,6,9

解释:交换 9 \texttt{9} 9 和 7 \texttt{7} 7。

数据范围

  • 1 ≤ arr.length ≤ 10 4 \texttt{1} \le \texttt{arr.length} \le \texttt{10}^\texttt{4} 1≤arr.length≤104
  • 1 ≤ arri ≤ 10 4 \texttt{1} \le \texttt{arri} \le \texttt{10}^\texttt{4} 1≤arri≤104

解法

思路和算法

用 n n n 表示数组 arr \textit{arr} arr 的长度。对于 0 ≤ i < j < n 0 \le i < j < n 0≤i<j<n,考虑交换 arr i \textit{arr}i arri 和 arr j \textit{arr}j arrj 之后的字典序变化。

  • 如果 arr i ≤ arr j \textit{arr}i \le \textit{arr}j arri≤arrj,则交换 arr i \textit{arr}i arri 和 arr j \textit{arr}j arrj 之后,下标 i i i 处的元素值不变或增加,因此字典序不可能更小。

  • 如果 arr i > arr j \textit{arr}i > \textit{arr}j arri>arrj,则交换 arr i \textit{arr}i arri 和 arr j \textit{arr}j arrj 之后,下标 i i i 处的元素值减少,因此字典序更小。

因此需要找到两个下标 left \textit{left} left 和 right \textit{right} right,满足 left < right \textit{left} < \textit{right} left<right 且 arr left > arr right \textit{arr}\\textit{left} > \textit{arr}\\textit{right} arrleft>arrright,才能在交换 arr left \textit{arr}\\textit{left} arrleft 和 arr right \textit{arr}\\textit{right} arrright 之后得到字典序更小的排列。

为了得到字典序小于原始数组的最大可能排列,应使用贪心策略找到上述下标 left \textit{left} left 和 right \textit{right} right。

  1. 寻找 left \textit{left} left 时,应寻找符合上述要求的最大下标。理由如下:当 left < right \textit{left} < \textit{right} left<right 且 arr left > arr right \textit{arr}\\textit{left} > \textit{arr}\\textit{right} arrleft>arrright 时,假设存在 left ′ \textit{left}' left′ 满足 left ′ < left \textit{left}' < \textit{left} left′<left 且 arr left ′ > arr right \textit{arr}\\textit{left}' > \textit{arr}\\textit{right} arrleft′>arrright,则交换 arr left \textit{arr}\\textit{left} arrleft 和 arr right \textit{arr}\\textit{right} arrright 与交换 arr left ′ \textit{arr}\\textit{left}' arrleft′ 和 arr right \textit{arr}\\textit{right} arrright 都可以得到字典序更小的排列,其中交换 arr left ′ \textit{arr}\\textit{left}' arrleft′ 和 arr right \textit{arr}\\textit{right} arrright 得到的排列的字典序更小,一定不是可能的最大排列。寻找 left \textit{left} left 的可行做法是寻找满足 arr i > arr i + 1 \textit{arr}i > \textit{arr}i + 1 arri>arri+1 的最大下标作为 left \textit{left} left,此时的 left \textit{left} left 为符合上述要求的最大下标。

  2. 在确定 left \textit{left} left 之后,记 leftNum = arr left \textit{leftNum} = \textit{arr}\\textit{left} leftNum=arrleft,寻找 right \textit{right} right 时,应在数组 arr \textit{arr} arr 的下标大于 left \textit{left} left 的所有元素中寻找小于 arr left \textit{arr}\\textit{left} arrleft 的最大元素所在的最小下标作为 right \textit{right} right。理由如下:记 rightNum = arr right \textit{rightNum} = \textit{arr}\\textit{right} rightNum=arrright,为了通过交换 arr left \textit{arr}\\textit{left} arrleft 和 arr right \textit{arr}\\textit{right} arrright 得到字典序更小的排列,必须满足 arr left > arr right \textit{arr}\\textit{left} > \textit{arr}\\textit{right} arrleft>arrright,在下标大于 left \textit{left} left 的所有元素中,下标小于 right \textit{right} right 的所有元素都小于 rightNum \textit{rightNum} rightNum,下标大于 right \textit{right} right 的所有元素都小于等于 rightNum \textit{rightNum} rightNum,如果与 arr left \textit{arr}\\textit{left} arrleft 交换的元素在下标 right \textit{right} right 的左侧则交换后位于下标 left \textit{left} left 处的元素一定小于 rightNum \textit{rightNum} rightNum,如果与 arr left \textit{arr}\\textit{left} arrleft 交换的元素在下标 right \textit{right} right 的右侧则交换后位于下标 right \textit{right} right 处的元素一定小于 leftNum \textit{leftNum} leftNum,因此如果与 arr left \textit{arr}\\textit{left} arrleft 交换的元素在下标 right \textit{right} right 的左侧或右侧则一定不是可能的最大排列。

具体做法如下。

  1. 反向遍历数组 arr \textit{arr} arr 并找到满足 arr i > arr i + 1 \textit{arr}i > \textit{arr}i + 1 arri>arri+1 的最大下标 i i i 作为下标 left \textit{left} left。如果不存在这样的下标 i i i,则记 left = − 1 \textit{left} = -1 left=−1。

  2. 当 left ≥ 0 \textit{left} \ge 0 left≥0 时,从下标 left + 1 \textit{left} + 1 left+1 开始正向遍历数组 arr \textit{arr} arr 并找到小于 arr left \textit{arr}\\textit{left} arrleft 的最大元素的最小下标作为 right \textit{right} right,然后交换 arr left \textit{arr}\\textit{left} arrleft 和 arr right \textit{arr}\\textit{right} arrright。

  3. 经过上述操作之后得到的 arr \textit{arr} arr 即为答案。

代码

java 复制代码
class Solution {
    public int[] prevPermOpt1(int[] arr) {
        int length = arr.length;
        int left = -1;
        for (int i = length - 2; i >= 0 && left < 0; i--) {
            if (arr[i] > arr[i + 1]) {
                left = i;
            }
        }
        if (left >= 0) {
            int right = left + 1;
            int rightMax = arr[left + 1];
            for (int i = left + 2; i < length; i++) {
                if (arr[i] < arr[left] && arr[i] > rightMax) {
                    right = i;
                    rightMax = arr[i];
                }
            }
            int temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
        }
        return arr;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 arr \textit{arr} arr 的长度。最多需要遍历数组两次。

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

相关推荐
lvwangshu20 小时前
CF1882D Tree XOR 题解
动态规划·题解·贪心·dp·树形dp·换根dp·性质题·思维好题·推导题
伟大的车尔尼2 天前
贪心题目:救生艇
贪心
Jasmine_llq2 天前
《P8816 [CSP-J 2022] 上升点列》
数据结构·算法·贪心·排序·二维动态规划 dp·类背包状态转移
lvwangshu3 天前
字符串 题解
字符串·trie·题解·贪心·技巧·性质题·思维好题
Tisfy8 天前
LeetCode 2333.最小差值平方和:排序 + 数学计算(不用二分)
数学·算法·leetcode·题解·贪心·排序
伟大的车尔尼8 天前
贪心题目:单调递增的数字
贪心
Tisfy8 天前
LeetCode 3727.最大交替平方和:排序 (双指针可选)
算法·leetcode·题解·贪心·数组·排序
jyyyx的算法博客12 天前
机器人开箱子【贪心 + 模拟】
贪心·模拟
伟大的车尔尼15 天前
贪心题目:使绳子变成彩色的最短时间
贪心