My LeetCode brush Title source [GitHub]: https://github.com/izhoujie/Algorithmcii
LeetCode 945. Make the array the only minimum increment
subject
Given an array of integers A, any A[i] is selected and incremented by 1 for each move operation.
Returns the minimum number of operations that make each value in A unique.
Example 1: Input: [1,2,2] Output: 1 Interpretation: After a move operation, the array will become [1, 2, 3]. Example 2: Input: [3,2,1,2,1,7] Output: 6 Interpretation: After six move s, the array will become [3, 4, 1, 2, 5, 7].
You can see that a move operation of five or fewer times cannot make each value of an array unique.
Tips: 0 <= A.length <= 40000 0 <= A[i] < 40000
Source: LeetCode
Links: https://leetcode-cn.com/problems/minimum-increment-to-make-array-unique
Copyright shall be owned by the withholding network.For commercial reprinting, please contact the official authorization. For non-commercial reprinting, please indicate the source.
Solving problems
Idea 1 - Sort first, then add up the +1 operands needed for each two neighbors from left to right;
- Arrays.sort(A) sort;
- Compare and record the operands needed to change the latter number to the previous number + 1.
Example: If the order followed by 123455, start with the second number:
- First time: 1223455 at this time move+=2-1
- Second time: 1233455 at this time move+=3-2
- Third time: 1234455 at this time move+=4-3
- Fourth time: 1234555 does not require operation
- Fifth time: 1234565 at this time move+=6-5
- Sixth time: 1234567 at this time move+=7-6
Where: - Time complexity: O(NlogN) N=A.length
- Spatial Complexity: O(1)
Idea 2 - First count the number of operands that are accumulated sequentially, and the number of times each + 1 operation is the same.
- Create a new array, new int[40001], and then count each number in A as a subscript;
- Traversing through the new array, if the same number in 1 is greater than 0, the number after -1 is the number that these numbers need to be operated on by +1. Add the number after these +1 operations to the next statistic, and make the final number different by -1 each time.
- After traversal, you need to check the number of maximum subscripts again. If the number is greater than 1, and -1 number needs to be + 1, you can use the sum formula of 1-n directly.
- Time complexity: O(N) N=max(A.length, max(A))
- Spatial Complexity: O(40001) or O(1)
Tips: The first step of the statistics actually implies sorting. Using the nature of natural numbers, the natural ordering of subscripts is a feature of arrays that can easily be ignored, such as letters (via char's -'a'operation) that go into arrays to avoid extra sorting.
Idea 3 - Path compression; (from LeetCode review area, nice...)
- Create a new array, new int[80000] (initialization value-1), because path compression is different from the statistics in method 2 (or statistical compression), where the operation of + 1 is compressed, but the number after + 1 needs a new array to record. If all the values in A are 39999, the final maximum number will be 79999;
- Start traversing and record the findPath of the route points. This is a recursive method, which may be more circular, analyzed separately:
private int findPath(int i) { // The first time a point is encountered, the value is recorded and returned, where j=0 if (path[i] == -1) { path[i] = i; return i; } else { // If I has a record, look backwards for the value at path[i] + 1 and eventually update the path value recursively path[i] = findPath(path[i] + 1); return path[i]; } }
For example: A{1, 1, 2, 3, 5, 5, 2, the corresponding path array initializes path values of -1
- 0 Subscript 1 has a path value of -1, updates to 1 after execution and returns 1, where move+=1-1, corresponding to 1 does not require a + 1 operation;
- 1 The path value of subscript 1 is marked as 1, so look backwards for the path value of 1+1. At this time, the path value of 2 found is -1, the value of update path 1 and 2 are both 2 and returned. At last, move+=2-1, 1 needs a + 1 operation.
- 2 Subscript 2 has a path value of 2. Finding a path of 2+1 backwards is worth -1. At this time, update the path values of path 1, 2, 3 to 3. Finally, move+=3-2 requires 1+1 operation for 2.
- 3 Subscript 3 has a path value of 3. Finding a path of 3+1 backwards is worth -1. At this time, update the path values of path 1, 2, 3, 4 to 4. Finally, move+=4-3 requires 1+1 operation for 3.
- 4 Subscript 5 has a path value of -1, updates to 5 after execution and returns 5, where move+=5-5, corresponding to 5 does not require a + 1 operation;
- 5 Subscript 5 has a path value of 5. Finding a path of 5+1 backwards is worth -1. At this time, the path values of path 1, 2, 3, 4, 5, 6 are all updated to 6. The last move+=6-5 requires a + 1 operation for 5.
- 6 Subscript 2 has a path value of 6. Looking backward for a path of 6+1 will result in -1. At this time, the path values of path 1, 2, 3, 4, 5, 6, 7 are updated to 7, and finally move+=7-2, which requires 5 + 1 operations for 2;
The so-called path compression actually records the maximum number of times the current traversal number has passed the + 1 operation, so that the maximum number can be found directly from the path later and the number of times the + 1 is needed is calculated on this basis.
Sample algorithm source
package leetcode; import java.util.Arrays; /** * @author ZhouJie * @date 2020 March 22, 2000 8:04:55 p.m. * @Description: 945. Minimum increment to make the array unique * */ public class LeetCode_0945 { } class Solution_0945 { /** * @author: ZhouJie * @date: 2020 8:08:06 p.m. on March 22, 2000 * @param: @param A * @param: @return * @return: int * @Description: 1-Sort first, then add up the +1 operands needed for each two neighbors from left to right; * Time complexity: O(NlogN) N=A.length * Spatial Complexity: O(1) * */ public int minIncrementForUnique_1(int[] A) { int len = 0, move = 0; if (A == null || (len = A.length) < 2) { return move; } Arrays.sort(A); for (int i = 1; i < len; i++) { // If the current value is less than or equal to the previous value, the + 1 operation is required, the number of times the + 1 operation is equal to the difference and then + 1, and the current value needs to be updated to the previous value + 1 if (A[i] <= A[i - 1]) { move += A[i - 1] - A[i] + 1; A[i] = A[i - 1] + 1; } } return move; } /** * @author: ZhouJie * @date: 2020 March 22, 2000 8:15:17 p.m. * @param: @param A * @param: @return * @return: int * @Description: 2-First count and then add up the operands from small to large. The number of times to add up + 1 is -1 of the same number. * In fact, this step of statistics implies sorting (the nature of natural numbers), which is the key reason why it is faster than the 1 method. * Time complexity: O(N) N=max(A.length, max(A)) * Spatial Complexity: O(40001) or O(1) */ public int minIncrementForUnique_2(int[] A) { int move = 0; if (A == null || A.length < 2) { return move; } // Since the maximum is 3999, if + 1 is 40000, 40,000 indexes are required int[] statistics = new int[40001]; // Maximum number of records used to traverse the right boundary of statistics int max = 0; for (int i : A) { statistics[i]++; max = Math.max(max, i); } max++; // max is the last possible maximum in A for (int i = 0; i < max; i++) { // If the number of statistics[i] in A is greater than 1, it means that the number of statistics[i]-1 needs to be + 1. // This step is just a + 1 operation for each of the statistics[i]-1 numbers, and the subsequent + 1 is handed over to statistics[i+1] to complete (recursively) if (statistics[i] > 1) { move += statistics[i] - 1; statistics[i + 1] += statistics[i] - 1; } } // If the number of statistics[max] is greater than 1, then statistics[max]-1 (counted as n) requires a + 1 operation; // This number needs to be + 1 in turn for the number of times 1, 2, 3, 4....n, that is, to sum 1-n, directly using the summation formula if (statistics[max] > 1) { int n = statistics[max] - 1; move += n * (n + 1) / 2; } return move; } /** * @author: ZhouJie * @date: 2020 March 22, 2000 8:36:13 p.m. * @param: @param A * @param: @return * @return: int * @Description: 1-Path compression; (from the LeetCode review area, nice...) * Time complexity: O(N) N=A.length * Spatial Complexity: O(80000) or O(1) * Since findPath can change values for multiple points at a time, it is not as efficient as Method 2. */ // If all numbers in A are equal and 39999, the maximum value will be 79999 when the + 1 operation is completed int[] path = new int[80000]; public int minIncrementForUnique_3(int[] A) { int move = 0; if (A == null || A.length < 2) { return move; } // -1 is an empty address mark, different from the median of A Arrays.fill(path, -1); for (int i : A) { int j = findPath(i); move += j - i; } return move; } /** * @author: ZhouJie * @date: 2020 March 22, 2000 8:49:55 p.m. * @param: @param i * @param: @return * @return: int * @Description: Path Compression Core * */ private int findPath(int i) { // The first time a point is encountered, the value is recorded and returned, where j=0 if (path[i] == -1) { path[i] = i; return i; } else { // If I has a record, look backwards for the value at path[i] + 1 and eventually update the path value recursively path[i] = findPath(path[i] + 1); return path[i]; } } }