LeetCode 945. Make the array the only minimum increment

Keywords: Java github network less

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;

  1. Arrays.sort(A) sort;
  2. 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.

  1. Create a new array, new int[40001], and then count each number in A as a subscript;
  2. 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.
  3. 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...)

  1. 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;
  2. 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];
		}
	}
}

Posted by tomato on Sun, 22 Mar 2020 10:52:55 -0700