Leecode1053
import java.util.ArrayList;
import java.util.List;
public class Leecode1053 {
/**
* 给你一个正整数数组 arr(可能存在重复的元素),请你返回可在 一次交换(交换两数字 arr[i] 和 arr[j] 的位置)后得到的、按字典序排列小于 arr 的最大排列。
* 如果无法这么操作,就请返回原数组
*
* 示例 1:
* 输入:arr = [3,2,1]
* 输出:[3,1,2]
* 解释:交换 2 和 1
*
*
* 示例 2:
* 输入:arr = [1,1,5]
* 输出:[1,1,5]
* 解释:已经是最小排列
*
*
* 示例 3:
* 输入:arr = [1,9,4,6,7]
* 输出:[1,7,4,6,9]
* 解释:交换 9 和 7
* 。
*/
/* 题解
记数组arr的长度为n, 对于0 <= i < n, 如果交换 arr[i] 和 arr[j]后得到的新数组按字典排列比原
数组小,显然有 arr[i] > arr[j]成立。因此符合题意要求的交换会使得数组arr在下标i处的元素变小。那么
为了得到按字典排序小于原来数组的最大新数组,尽可能地保持前面的元素不变是最优的,即让i最大化。
1. 如何最大化i
我们可以从大到小枚举 i ∈ [0, n - 2], 然后枚举 j ∈ [i + 1, n], 如果存在j使 arr[i] >
arr[j] 成立,那么说明当前枚举的i是最大化的。这里只需要判断 arr[i] > arr[i + 1] 是
否成立即可, 那么说明当前枚举的i是最大化的。这里只需要判断 arr[i] > arr[i + 1] 是否
成立即可,因为后面的元素是符合非递减的,即 arr[i + 1]是arr[i] 后面的最小元素。
-- 当我们枚举i时,显然不存在k > i, 使得 arr[k] > arr[k + 1]成立,因此 arr[i]后面
的元素是符合非递减的。
2. i 最大化后, j ∈ [i + 1, n]应该怎么选择
显然在满足 arr[j] < arr[i] 的条件下,取最大的arr[j]是最优的。但是题目并没有元素不重
复的要求,最大的arr[j]可能有重复值,那么选择其中下标最小的arr[j]是最优的。
由前面推导可知,区间[i + 1,n)的元素是非递减的,因此我们从大到小枚举j, 直到 arr[j] < arr[i]
且 arr[j] ≠ arr[j - 1]成立, 那么得到的j就是符合要求的。交换arr[i] 和 arr[j]。
*/
/**
* 方法一
* @param A
* @return
*/
public int[] prevPermOpt1_1(int[] A) {
int len = A.length;
int curMax = -1;
int index = -1;
boolean hasResult = false;
for (int i = len - 2; i >= 0; i--) {
if (A[i+1] < A[i]) { // 此处逆序,需要移动A[i]
for (int j = i + 1; j < len; j++) { // 寻找与 A[i] 交换的位置
if (A[i] > A[j]) { // 必须满足 A[i] > A[j],否则不能满足交换后的字典序小于原始字典序
hasResult = true;
if (A[j] > curMax) {
curMax = A[j];
index = j;
}
}
}
if (hasResult) {
int tmp = A[i];
A[i] = A[index];
A[index] = tmp;
return A;
}
}
}
return A;
}
/**
*
* @param arr
* @return
*/
public int[] prevPermOpt1_2(int[] arr) {
int n = arr.length;
for (int i = n - 2; i >= 0; i--) {
if (arr[i] > arr[i + 1]) {
int j = n - 1;
while (arr[j] >= arr[i] || arr[j] == arr[j - 1]) {
j--;
}
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
break;
}
}
return arr;
}
/**
* 输出
* @param ints
* @return
*/
public static List createList(int[] ints) {
List<Integer> arrayList = new ArrayList();
StringBuilder sBuil = new StringBuilder();
for (int anInt : ints) {
arrayList.add(anInt);
}
return arrayList;
}
public static void main(String[] args) {
Leecode1053 leecode1053 = new Leecode1053();
// 方法一测试
int[] a = new int[]{1,9,4,6,7};
int[] ints1 = leecode1053.prevPermOpt1_1(a);
System.out.println(createList(ints1));
// 方法二测试
int[] b = new int[]{1,9,4,6,7};
int[] ints2 = leecode1053.prevPermOpt1_2(b);
System.out.println(createList(ints2));
}
}