'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

  1. Not optimistic/Less performant
  2. Failing on edge cases
  3. 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 and min

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