文章目录
题目
标题和出处
标题:交换一次的先前排列
难度
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。
-
寻找 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 为符合上述要求的最大下标。
-
在确定 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 的左侧或右侧则一定不是可能的最大排列。
具体做法如下。
-
反向遍历数组 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。
-
当 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。
-
经过上述操作之后得到的 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)。