交换一次的先前排列
难度:
标签:
题目描述
代码结果
运行时间: 29 ms, 内存: 16.7 MB
/*
* Problem: Given a positive integer array arr (which may contain duplicate elements), return the maximum permutation that is lexicographically smaller than arr after exactly one swap (swap the positions of two numbers arr[i] and arr[j]).
* If it is not possible to do so, return the original array.
*
* Approach:
* 1. Traverse the array from right to left to find the first pair where arr[i] > arr[i+1]. This pair indicates the point where the order starts decreasing.
* 2. If such a pair is found, traverse again from right to left to find the first element that is smaller than arr[i]. This element will be arr[j].
* 3. Swap arr[i] and arr[j].
* 4. If no such pair is found, return the original array as it is already the smallest permutation.
*/
import java.util.*;
import java.util.stream.*;
public class Solution {
public int[] prevPermOpt1(int[] arr) {
List<Integer> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
int n = list.size();
for (int i = n - 2; i >= 0; i--) {
if (list.get(i) > list.get(i + 1)) {
for (int j = n - 1; j > i; j--) {
if (list.get(j) < list.get(i)) {
while (j > 0 && list.get(j).equals(list.get(j - 1))) {
j--;
}
Collections.swap(list, i, j);
return list.stream().mapToInt(k -> k).toArray();
}
}
}
}
return arr;
}
}
解释
方法:
此题解采用的是从后往前扫描的贪心算法。首先,从倒数第二个元素开始向前扫描,寻找第一个比其后一个元素大的元素,记为arr[i]。这是因为我们需要找到一个尽可能靠后的位置,使得交换后得到的排列字典序小于原排列。一旦找到这样的元素,就在其右侧找到一个比它小但尽可能大的元素,记为arr[j]。为了确保交换后的排列是小于原排列的最大排列,我们需要在i右侧找到最右边的、比arr[i]小的元素。此外,还需要确保arr[j]不等于arr[j-1],以避免重复元素导致的问题。找到合适的i和j后,交换arr[i]和arr[j],然后返回修改后的数组。
时间复杂度:
O(n^2)
空间复杂度:
O(1)
代码细节讲解
🦆
为什么要从数组的倒数第二个元素开始向前扫描,而不是从首元素或中间部分开始?
▷🦆
在找到第一个比后一个元素大的元素后,为何选择在其右侧寻找比它小但尽可能大的元素,这样的选择标准是基于什么考虑?
▷🦆
在内层循环中,条件 `arr[j] >= arr[i] or arr[j] == arr[j - 1]` 是如何确保找到的 `arr[j]` 既小于 `arr[i]` 又是最大的可交换值?
▷🦆
题解中提到需要确保 `arr[j]` 不等于 `arr[j-1]` 来避免重复元素导致的问题,能否详细解释一下这里的问题是什么?
▷