[LeetCode] 1053. Previous Permutation With One Swap

Given an array of positive integers arr (not necessarily distinct), return the
lexicographically largest permutation that is smaller than arr, that can be made with exactly one swap. If it cannot be done, then return the same array.

Note that a swap exchanges the positions of two numbers arr[i] and arr[j]

Example 1:
Input: arr = [3,2,1]
Output: [3,1,2]
Explanation: Swapping 2 and 1.

Example 2:
Input: arr = [1,1,5]
Output: [1,1,5]
Explanation: This is already the smallest permutation.

Example 3:
Input: arr = [1,9,4,6,7]
Output: [1,7,4,6,9]
Explanation: Swapping 9 and 7.

Constraints:
1 <= arr.length <= 104
1 <= arr[i] <= 104

交换一次的先前排列。

给你一个正整数数组 arr(可能存在重复的元素),请你返回可在 一次交换(交换两数字 arr[i] 和 arr[j] 的位置)后得到的、按字典序排列小于 arr 的最大排列。
如果无法这么操作,就请返回原数组。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/previous-permutation-with-one-swap
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路一,treemap

这道题有两种思路,一种会利用到 treemap,一种是线性解法。这道题是在找比当前 input 给的 permutation 小的所有 permutation 里面最大的那个。

这道题的核心在于必须从右往左扫描 input 数组,从左往右是不行的。至于到底是哪两个数字 swap,其中一个一定是找一个下降的位置中较大的那个数字,另一个数字则视情况而定。这一类的题可以有多种变化。

首先 treemap 的做法,对于当前的 input 数组给的 permutation,如果我们要找一个比他小的 permutation,我们首先需要找到一个下降的地方,比如第一个例子里的3 - 2,然后我们将 2 替换成比 2 小的数字里最大的那个(如果有的话)。按照这个例子就是 1。

所以我们可以反过来思考,我们从右往左扫描,对于当前的数字,如果 treemap 里存在一个 lowerKey,那么就把当前数字和 lowerKey 进行 swap 即可。

复杂度

时间O(nlogn)
空间O(n)

Java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public int[] prevPermOpt1(int[] arr) {
TreeMap<Integer, Integer> map = new TreeMap<>();
int len = arr.length;
map.put(arr[len - 1], len - 1);
for (int i = arr.length - 2; i >= 0; i--) {
Integer lower = map.lowerKey(arr[i]);
if (lower != null) {
int j = map.get(lower);
swap(arr, i, j);
break;
}
map.put(arr[i], i);
}
return arr;
}

private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}

思路二,类似next permutation

treemap 的解法是利用到 treemap 自动排序的特点。这里我们线性的解法则可以省去这个排序的步骤。还是从右往左扫描,按照题意我们需要找一个拐点。如果从右往左扫描下去一直是下坡的话(arr[i - 1] < arr[i]),则一直往左走;当我们找到一个拐点的时候,则停下,此时的 arr[i - 1] 是参与 swap 的元素之一。此时我们再次从右往左扫描,找到的第一个比 arr[i - 1] 小的元素是参与 swap 的另一个元素。把两者 swap 即可。

复杂度

时间O(n)
空间O(1)

Java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public int[] prevPermOpt1(int[] arr) {
int n = arr.length;
int a = n - 1;
int b = n - 1;
for (int i = n - 1; i >= 1; i--) {
if (arr[i - 1] > arr[i]) {
a = i - 1;
break;
}
}

for (int i = n - 1; i >= 1; i--) {
if (arr[i] == arr[i - 1]) {
continue;
}
if (arr[i] < arr[a]) {
b = i;
break;
}
}
swap(arr, a, b);
return arr;
}

private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}

[LeetCode] 1053. Previous Permutation With One Swap
https://shurui91.github.io/posts/2013378145.html
Author
Aaron Liu
Posted on
August 16, 2021
Licensed under