解决方案

步骤

1

如果从右往左一直递减,则无法交换,直接返回

2

找到从右往左 第一个 打破递减规律的,作为左边的要交换的数

3

找到从右往左 第一个 小于 选中的左边的数的,作为右边要交换的数

4

如果找到的右边的数的左边有跟它一样的数,那么要选择最左边跟它等值的,作为右边要交换的数

分析

整个题就是要找两个数字进行交换,所以我们找找规律

递减性

观察

样例中,成功交换的两个数,貌似都是 从右往左 在打破递减规律时,那个位置要互换

[3, 2, 1]
    ^  ^
2 是 从右往左 第一个 不递减的
[1, 1, 5]
没有不递减的,所以互换没收益
[1, 9, 4, 6, 7]
    ^        ^
9 是 从右往左 第一个 不递减的
[3, 1, 1, 3]
 ^  ^
3 是 从右往左 第一个 不递减的
然而右边要交换的 不是最右边的 3,而是 1
看来,这块需要之后推理找规律
推测

根据上面的观察,我们可以假设

交换的 两个数的左边的那个数,一定是 从右往左 打破递减规律的 第一个数

右边需要交换的数字

观察

当我们找到 要交换的左边的那个数字时,可能会跟 最右侧的数字 产生三种情况

情况 1

右边的 小于 左边的

比如 413 换成 314,还不错

情况 2

右边的 等于 左边的

比如 313 换成 313

这样不行,因为题目要求交换后,要小于 交换前的数

情况 2

右边的 大于 左边的

比如 213 换成 312

这样也不行,因为题目要求交换后,要小于 交换前的数

推测

所以得从右边往左边找,第一个 小于 左边那个数字的

也就是说 313 的右边得选 1, 也就是 换成 133

213 的右边也得选 小于 2 的 1,换成 123

另外一个特殊情况

观察

如果我们的数据是 3114,

依照我们上面的规则,那就是 左边的要交换的是 3。

右边的要交换的是 十位数的 1,

那么我们会得到 1134

然而,如果左边跟 百位数的 1 交换,会更好

会得到 1314,比 1134 要大,且小于 3114

推测

所以我们猜测,当确定右边的数字后,如果这个数字的左边有一样的,那么就选择最左边一样的那个

Java 代码

Runtime: 0 ms

Memory Usage: 39.7 MB

Beats 100.00% of java submissions.

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 32 33 34 35 36 37 38 39 40 41 42
public class Solution { public int[] prevPermOpt1(int[] A) { // 1. 边界情况处理 if (A.length == 1) { return A; } // 2. 找左边要交换的位置 int left = A.length - 2; while (left >= 0) { if (A[left] > A[left + 1]) { break; } left--; } // 2.1 如果一直递减 if (left < 0) { return A; } // 3. 找右边 第一个小于 left 的值 int right = A.length - 1; while (A[right] >= A[left]) { right--; } // 4. 向左错位,如果 right 的左边 跟 right 一样 int rightValue = A[right]; while (A[right] == rightValue) { right--; } right++; // 5. 交换 int temp = A[left]; A[left] = A[right]; A[right] = temp; return A; }}

Java Script 代码

Runtime: 100 ms

Memory Usage: 38.8 MB

Beats 83.08% of javascript submissions.

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 32 33 34 35 36 37 38 39 40 41 42
public class Solution { public int[] prevPermOpt1(int[] A) { // 1. 边界情况处理 if (A.length == 1) { return A; } // 2. 找左边要交换的位置 int left = A.length - 2; while (left >= 0) { if (A[left] > A[left + 1]) { break; } left--; } // 2.1 如果一直递减 if (left < 0) { return A; } // 3. 找右边 第一个小于 left 的值 int right = A.length - 1; while (A[right] >= A[left]) { right--; } // 4. 向左错位,如果 right 的左边 跟 right 一样 int rightValue = A[right]; while (A[right] == rightValue) { right--; } right++; // 5. 交换 int temp = A[left]; A[left] = A[right]; A[right] = temp; return A; }}

Python 代码

Runtime: 140 ms

Memory Usage: 14.3 MB

Beats 99.82% of javascript submissions.

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 32
class Solution: def prevPermOpt1(self, A: List[int]) -> List[int]: # 1. 边界情况处理 if len(A) == 1 : return A # 2. 找左边要交换的位置 left = len(A) - 2 while left >= 0 : if A[left] > A[left + 1] : break left -= 1 else : # 2.1 如果一直递减 return A # 3. 找右边 第一个小于 left 的值 right = len(A) - 1 while A[right] >= A[left] : right -= 1 # 4. 向左错位,如果 right 的左边 跟 right 一样 right_value = A[right] while (A[right] == right_value) : right -= 1 right += 1 # 5. 交换 A[left], A[right] = A[right], A[left] return A

ZZAX 微信公众

文档一更新,立刻告诉你