题目
标题和出处
标题:交换一次的先前排列
难度
5 级
题目描述
要求
给定一个正整数数组 arr \texttt{arr} arr(其中的元素可能重复),返回可在一次交换(交换两数字 arr[i] \texttt{arr[i]} arr[i] 和 arr[j] \texttt{arr[j]} arr[j] 的位置)后得到的按字典序排列小于 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 ≤ arr[i] ≤ 10 4 \texttt{1} \le \texttt{arr[i]} \le \texttt{10}^\texttt{4} 1≤arr[i]≤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] arr[i] 和 arr [ j ] \textit{arr}[j] arr[j] 之后的字典序变化。
-
如果 arr [ i ] ≤ arr [ j ] \textit{arr}[i] \le \textit{arr}[j] arr[i]≤arr[j],则交换 arr [ i ] \textit{arr}[i] arr[i] 和 arr [ j ] \textit{arr}[j] arr[j] 之后,下标 i i i 处的元素值不变或增加,因此字典序不可能更小。
-
如果 arr [ i ] > arr [ j ] \textit{arr}[i] > \textit{arr}[j] arr[i]>arr[j],则交换 arr [ i ] \textit{arr}[i] arr[i] 和 arr [ j ] \textit{arr}[j] arr[j] 之后,下标 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}] arr[left]>arr[right],才能在交换 arr [ left ] \textit{arr}[\textit{left}] arr[left] 和 arr [ right ] \textit{arr}[\textit{right}] arr[right] 之后得到字典序更小的排列。
为了得到字典序小于原始数组的最大可能排列,应使用贪心策略找到上述下标 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}] arr[left]>arr[right] 时,假设存在 left ′ \textit{left}' left′ 满足 left ′ < left \textit{left}' < \textit{left} left′<left 且 arr [ left ′ ] > arr [ right ] \textit{arr}[\textit{left}'] > \textit{arr}[\textit{right}] arr[left′]>arr[right],则交换 arr [ left ] \textit{arr}[\textit{left}] arr[left] 和 arr [ right ] \textit{arr}[\textit{right}] arr[right] 与交换 arr [ left ′ ] \textit{arr}[\textit{left}'] arr[left′] 和 arr [ right ] \textit{arr}[\textit{right}] arr[right] 都可以得到字典序更小的排列,其中交换 arr [ left ′ ] \textit{arr}[\textit{left}'] arr[left′] 和 arr [ right ] \textit{arr}[\textit{right}] arr[right] 得到的排列的字典序更小,一定不是可能的最大排列。寻找 left \textit{left} left 的可行做法是寻找满足 arr [ i ] > arr [ i + 1 ] \textit{arr}[i] > \textit{arr}[i + 1] arr[i]>arr[i+1] 的最大下标作为 left \textit{left} left,此时的 left \textit{left} left 为符合上述要求的最大下标。
-
在确定 left \textit{left} left 之后,记 leftNum = arr [ left ] \textit{leftNum} = \textit{arr}[\textit{left}] leftNum=arr[left],寻找 right \textit{right} right 时,应在数组 arr \textit{arr} arr 的下标大于 left \textit{left} left 的所有元素中寻找小于 arr [ left ] \textit{arr}[\textit{left}] arr[left] 的最大元素所在的最小下标作为 right \textit{right} right。理由如下:记 rightNum = arr [ right ] \textit{rightNum} = \textit{arr}[\textit{right}] rightNum=arr[right],为了通过交换 arr [ left ] \textit{arr}[\textit{left}] arr[left] 和 arr [ right ] \textit{arr}[\textit{right}] arr[right] 得到字典序更小的排列,必须满足 arr [ left ] > arr [ right ] \textit{arr}[\textit{left}] > \textit{arr}[\textit{right}] arr[left]>arr[right],在下标大于 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}] arr[left] 交换的元素在下标 right \textit{right} right 的左侧则交换后位于下标 left \textit{left} left 处的元素一定小于 rightNum \textit{rightNum} rightNum,如果与 arr [ left ] \textit{arr}[\textit{left}] arr[left] 交换的元素在下标 right \textit{right} right 的右侧则交换后位于下标 right \textit{right} right 处的元素一定小于 leftNum \textit{leftNum} leftNum,因此如果与 arr [ left ] \textit{arr}[\textit{left}] arr[left] 交换的元素在下标 right \textit{right} right 的左侧或右侧则一定不是可能的最大排列。
具体做法如下。
-
反向遍历数组 arr \textit{arr} arr 并找到满足 arr [ i ] > arr [ i + 1 ] \textit{arr}[i] > \textit{arr}[i + 1] arr[i]>arr[i+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}] arr[left] 的最大元素的最小下标作为 right \textit{right} right,然后交换 arr [ left ] \textit{arr}[\textit{left}] arr[left] 和 arr [ right ] \textit{arr}[\textit{right}] arr[right]。
-
经过上述操作之后得到的 arr \textit{arr} arr 即为答案。
代码
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)。
本文讲解如何通过贪心策略求解‘交换一次的先前排列’问题:给定正整数数组,仅允许一次元素交换,得到字典序小于原数组的最大排列。核心思路是反向找首个下降位置left,再在右侧找小于arr[left]的最大值的最小下标right,交换二者。时间复杂度O(n),空间复杂度O(1)。
1078

被折叠的 条评论
为什么被折叠?



