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
交换一次的先前排列。
给你一个正整数的数组 A(其中的元素不一定完全不同),请你返回可在 一次交换(交换两数字 A[i] 和 A[j] 的位置)后得到的、按字典序排列小于 A 的最大可能排列。
如果无法这么操作,就请返回原数组。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/previous-permutation-with-one-swap
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这道题有两种思路,一种会利用到 treemap,一种是线性解法。这道题有点像在找下一个 permutation,但是这个 permutation 还不是下一个,而是比当前 input 给的 permutation 小的所有 permutation 里面最大的那个。
这道题的核心在于无论是哪种做法,都必须是从右往左扫描 input 数组,从左往右是不行的。首先 treemap 的做法,对于当前的 input 数组给的 permutation,如果我们要找一个比他小的 permutation,我们首先需要找到一个下降的地方,比如第一个例子里的3 - 2,然后我们将 2 替换成比 2 小的数字里最大的那个(如果有的话)。按照这个例子就是 1。所以我们可以反过来思考,我们从右往左扫描,对于当前的数字,如果 treemap 里存在一个 lowerKey,那么就把当前数字和 lowerKey 进行 swap 即可。
时间O(nlogn)
空间O(n)
Java实现
1 class Solution { 2 public int[] prevPermOpt1(int[] arr) { 3 TreeMap<Integer, Integer> map = new TreeMap<>(); 4 int len = arr.length; 5 map.put(arr[len - 1], len - 1); 6 for (int i = arr.length - 2; i >= 0; i--) { 7 Integer lower = map.lowerKey(arr[i]); 8 if (lower != null) { 9 int j = map.get(lower); 10 swap(arr, i, j); 11 break; 12 } 13 map.put(arr[i], i); 14 } 15 return arr; 16 } 17 18 private void swap(int[] arr, int i, int j) { 19 int temp = arr[i]; 20 arr[i] = arr[j]; 21 arr[j] = temp; 22 } 23 }
再来是线性的解法。刚才 treemap 的解法是利用到 treemap 自动排序的特点。这里我们线性的解法则可以省去这个排序的步骤。还是从右往左扫描,按照题意我们需要找一个拐点。如果扫描下去一直是下坡的话(arr[i - 1] < arr[i]),则一直往下走;当我们找到一个拐点的时候,则停下,此时的 arr[i - 1] 是参与 swap 的元素之一。此时我们再次从右往左扫描,找到的第一个比 arr[i - 1] 小的元素是参与 swap 的另一个元素。把两者 swap 即可。
时间O(n)
空间O(1)
Java实现
1 class Solution { 2 public int[] prevPermOpt1(int[] arr) { 3 int len = arr.length; 4 int a = len - 1; 5 int b = len - 1; 6 for (int i = len - 1; i >= 1; i--) { 7 if (arr[i - 1] > arr[i]) { 8 a = i - 1; 9 break; 10 } 11 } 12 13 for (int i = len - 1; i > a; i--) { 14 if (arr[i] == arr[i - 1]) { 15 continue; 16 } 17 if (arr[i] < arr[a]) { 18 b = i; 19 break; 20 } 21 } 22 swap(arr, a, b); 23 return arr; 24 } 25 26 private void swap(int[] arr, int i, int j) { 27 int temp = arr[i]; 28 arr[i] = arr[j]; 29 arr[j] = temp; 30 } 31 }