'Minimum moves to transform a list so that each element equals its own frequency
Given a sorted array like [1,2,4,4,4]
. Each element in the array should occur exactly same number of times as element. For Example 1 should occur 1 time, 2 should occur 2 times, n should occur n times, After minimum number of moves, the array will be rendered as [1,4,4,4,4]
Original Shifted Min Moves Reason
[1,2,4,4,4] [1,4,4,4,4] 2 delete 2 and insert 4
[1, 1, 3, 4, 4, 4] [1,4,4,4,4] 3 delete 1,3 an insert 4
[10, 10, 10] [] 3 delete all 10s
[1, 1, 1, 1, 3, 3, 4, 4, 4, 4, 4] [1, 3, 3, 3, 4, 4, 4, 4] 5 delete 1,1,1,4 and insert 3
We are allowed to delete/drop some elements totally, as explained above.
I need to find the minimum number of rotations/moves required to get the shifted version. So, I have written below logic to find the min number of moves required on original array.
public static int minMoves(int[] data) {
Map<Integer, Integer> dataMap = new HashMap<>();
for(int i=0; i<data.length;i++) {
if(dataMap.containsKey(data[i])) {
dataMap.put(data[i], dataMap.get(data[i])+1);
}else {
dataMap.put(data[i], 1);
}
}
System.out.println("Map - "+dataMap);
int[] minOperations = {0};
dataMap.forEach((digit,frequency)->{
if(digit!=frequency) {
if(digit<frequency) {
minOperations[0] += (frequency-digit);
}else if(digit>frequency && (digit-frequency)<=minOperations[0]) {
minOperations[0] += digit-frequency;
}else {
minOperations[0] += frequency;
}
}
System.out.println(digit+" "+frequency+" "+minOperations[0]);
});
return minOperations[0];
}
The above logic is working fine for above stated cases. But it's failing for few cases.
Failed Case - [1, 2, 2, 2, 5, 5, 5, 8] should return 4, But it's returning 5.
I feel my solution has few problems
- Not optimistic/Less performant
- Failing on edge cases
- Not following any algorithmic approach
Please help to point out. What is the right approaches/algorithms available to solve this in more efficient manner.
More Explanation:
There is an array A of N integers sorted in non-decreasing order. In one move, you can either remove an integer from A or insert an integer before or after any element of A. The goal is to achieve an array in which all values X that are present in the array occur exactly X times.
Given A = [1, 2, 2, 2, 5, 5, 5, 8], your function should retum 4. You can delete the 8 and one occurrence of 2, and insert 5 twice, resulting in [1, 2, 2, 5, 5, 5, 5, 5] after four moves. Notice that after the removals, there is no occurrence of 8 in the array anymore.
given an array A retums the minimum number of moves after which every value X in the array occurs exactly X times. Note that it is permissible to remove some values entirely, if appropriate.
What is the minimum number of moves after which every value X in the array occurs exactly X times?
Solution 1:[1]
The error is in this comparison:
(digit-frequency)<=minOperations[0]
There is no logic in comparing with the intermediate, running sum minOperations[0]
. It should compare the following two:
How many insert operations are needed to get to the required number of digits -- this is the left side of the comparison (OK)
How many delete operations are needed to remove all these digits, this is
frequency
So that comparison should be:
digit - frequency <= frequency
Some other remarks:
As you say the input is sorted, there is no need to create a Map. You can immediately count the number of digits as you iterate from left to right through the array, and at the same time update
minOperations
.There is no need to make
minOperations
an array. It can be just a primitive.The number of expressions to check can be reduced a bit, by using
abs
andmin
Here is how you could update your code:
import java.lang.Math.*;
// ...
public static int minMoves(int[] data) {
int minOperations = 0;
for(int i = 0, j = 0; i < data.length; i = j) {
while (j < data.length && data[i] == data[j]) j++;
int frequency = j - i;
minOperations += Math.min(Math.abs(data[i] - frequency), frequency);
}
return minOperations;
}
Solution 2:[2]
Try this :
public static int minMovements(Integer[] array) {
int numberOfMoves = 0;
List<Integer> arrayList = List.of(array);
List<Integer> uniques = arrayList.stream().distinct().collect(Collectors.toList());
for (Integer e : uniques) {
int ocurrences = (int) arrayList.stream().filter(o -> o == e).count();
numberOfMoves += Integer.min(Math.abs(e - ocurrences), ocurrences);
}
return numberOfMoves;
}
Solution 3:[3]
private static void findMinMoves(int[] data) {
Map<Integer, Integer> dataMap = new HashMap<>();
for (int i = 0; i < data.length; i++) {
if (dataMap.containsKey(data[i])) {
dataMap.put(data[i], dataMap.get(data[i]) + 1);
} else {
dataMap.put(data[i], 1);
}
}
int[] minOperations = {0};
dataMap.forEach((digit, occurrence) -> {
if (digit != occurrence) {
if (digit < occurrence) {
minOperations[0] += (occurrence - digit);
} else if (digit > occurrence) {
minOperations[0] += Math.min(Math.abs(occurrence - digit), occurrence);
} else {
minOperations[0] += occurrence;
}
}
});
System.out.println("Minimum occurrences" + minOperations[0]);
}
Time Complexity : O(n) Space complexity: O(n)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | trincot |
Solution 2 | Saiprashanth |
Solution 3 | aakash kumar |