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 (A swap exchanges the positions of two numbers arr[i]
and arr[j]
). If it cannot be done, then return the same array.
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.
Example 4:
Input: arr = [3,1,1,3]
Output: [1,3,1,3]
Explanation: Swapping 1 and 3.
Constraints:
1 <= arr.length <= 104
1 <= arr[i] <= 104
這道題給了一個正整數的數組,說是讓任意交換兩個數字,使得變成字母順序最大的一種全排列,但是需要小於原先的排列,若無法得到這樣的全排列(說明當前已經是最小的全排列),則返回原數組。通過分析題目中給的例子不難理解題意,根據例子2來看,若給定的數組就是升序排列的,則無法得到更小的全排列,說明只有遇到降序的位置的時候,才有可能進行交換。但是假如有多個可以下降的地方呢,比如例子1,3到2下降,2到1下降,這里是需要交換2和1的,所以最好是從后往前檢驗,遇到前一個數字比當前數字大的情況時,前一個數字必定是交換方之一,而當前數字並不是。比如例子3,數字4的前面是9,正確結果是9和7交換,所以還要從4往后遍歷一下,找到一個僅次於9的數字交換才行,而且數字相同的話,取坐標較小的那個,比如例子4就是這種情況。其實這道題的四個例子給的真不錯,基本各種情況都 cover 到了,贊一個~ 分析到這里,代碼就不難寫了,首先從后往前遍歷,假如當前數字大於等於前一個數字,直接跳過,否則說明需要交換的。從當前位置再向后遍歷一遍,找到第一個僅次於拐點的數字交換即可,注意下面的代碼雖然嵌套了兩個 for 循環,其實是線性的時間復雜度,參見代碼如下:
class Solution {
public:
vector<int> prevPermOpt1(vector<int>& arr) {
int n = arr.size(), mx = 0, idx = -1;
for (int i = n - 1; i > 0; --i) {
if (arr[i] >= arr[i - 1]) continue;
for (int j = i; j < n; ++j) {
if (arr[j] < arr[i - 1] && mx < arr[j]) {
mx = arr[j];
idx = j;
}
}
swap(arr[i - 1], arr[idx]);
break;
}
return arr;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1053
參考資料:
https://leetcode.com/problems/previous-permutation-with-one-swap/
https://leetcode.com/problems/previous-permutation-with-one-swap/discuss/838836/Java-O(n)