简体   繁体   中英

What could make this code more efficient (for Hackerrank problem)?

See this code I have written to solve this hackerrank challenge.

https://www.hackerrank.com/challenges/frequency-queries/problem?h_l=interview&playlist_slugs%5B%5D=interview-preparation-kit&playlist_slugs%5B%5D=dictionaries-hashmaps

Essentially, at this point, I KNOW that my code is algorithmically fast enough to be acceptable for this challenge, because what I have done is EXACTLY what is discussed in the editorial section. However, the code terminates due to timeout. Therefore, I'm wondering whether theres some glaring inefficiencies in the data structures I used or if it could be a memory problem or something. Also, it is worth noting that the editorial is written in c++ and I have written my code in java

I'll describe what's going on. Queries is the list of queries to perform. countMap is a HashMap that will store how many times a given number appears in the array. freqMap is a HashMap that stores how many instances there are of a number appearing x (key) times in the array. So for example, if I add the number 5 to the array, I will add 1 to the value at key 5 in countMap. Then, if the value at key 5 in countMap is now 6, meaning that 5 occurs 6 times in the array, I will add 1 to the value at key 6 in the freqMap, since a number occurs 6 times in the array. Then, if a query happens to check if there are any numbers that appear exactly 6 times in the array, I can check directly in freqMap, and if the value at key 6 is >0, I add the number 1 to the result ArrayList. Else, I add a 0 to the result ArrayList.

static List<Integer> freqQuery(List<List<Integer>> queries) {

    HashMap<Integer,Integer> countMap = new HashMap();
    HashMap<Integer,Integer> freqMap = new HashMap();
    List<Integer> res = new ArrayList();
    for(int i = 0; i < queries.size(); i++){
        List<Integer> query = queries.get(i);
        Integer qType = query.get(0);
        Integer qVal = query.get(1);
        if(qType == 1){
            Integer countVal = countMap.get(qVal);
            if(countVal == null){
                countVal = new Integer(0);
            }
            countVal += 1;
            countMap.put(qVal,countVal);
            Integer freqVal = freqMap.get(countVal);
            if(freqVal == null){
                freqVal = new Integer(0);
            }
            freqVal += 1;
            freqMap.put(countVal,freqVal);
            if ((countVal - 1) > 0) {
                Integer prevFreqVal = freqMap.get(countVal - 1);
                prevFreqVal -= 1;
                freqMap.put((countVal -1),prevFreqVal);
            }
        }else if(qType == 2){
            Integer countVal = countMap.get(qVal);
            if(countVal != null && countVal != 0){
                countVal -= 1;
                countMap.put(qVal,countVal);
                Integer freqVal = freqMap.get(countVal);
                if(freqVal == null){
                    freqVal = new Integer(0);
                }
                freqVal += 1;
                freqMap.put(countVal,freqVal);
                Integer prevFreqVal = freqMap.get(countVal + 1);
                prevFreqVal -= 1;
                freqMap.put((countVal + 1),prevFreqVal);
            }
        }else if(qType == 3){
            Integer freqVal = freqMap.get(qVal);
            if(freqVal == null || freqVal < 1){
                res.add(0);
            }else{
                res.add(1);
            }
        }
    }
    return res;

}

All the results are correct, but I'm getting a timeout error, indicating that my code is not fast enough. I'm lost as to why this is.

HashMap get and put operations are O(1), iterating through the queries once is obviously O(q). Adding to the end of arraylist seems to be O(n), where n is length of arraylist.

Well stupid me didn't check the discussion section and apparently the boilerplate code for reading in the queries was too slow, mostly due to what's already been discussed to be using get() from a List is apparently O(n) complexity. On top of this, for add() for the result, one should use a LinkedList as opposed to an ArrayList for O(1) complexity.

The changes I made were to actually change from a List<List<Integer>> to a List<int[]> , thereby accessing the query and value in constant time. I could also get even faster by simply using a 2-d array if the input lengths are already known.

Also, while reading in the input, I kept count of how many queries there were that required me to actually add a value to my result List, say n, and passed that to my method. With that knowledge, I could instantiate an array int[n] and then add to the result array in constant time as well. Much faster

A bit cleaner and closer to what you were doing:

static List<Integer> freqQuery(List<List<Integer>> queries) {
    Map<Integer, Integer> countMap = new HashMap<>();
    Map<Integer, Integer> freqMap = new HashMap<>();
    List<Integer> res = new LinkedList<>();
    for (List<Integer> query : queries) {
        Integer qType = query.get(0);
        Integer qVal = query.get(1);
        switch (qType) {
        case 1:
            Integer countVal = countMap.merge(qVal, 1, Integer::sum);
            freqMap.merge(countVal, 1, Integer::sum);
            freqMap.computeIfPresent(countVal - 1, (key, value) -> value - 1);
        case 2:
            countMap.computeIfPresent(qVal, (key, value) -> {
                freqMap.merge(value, -1, Integer::sum);
                freqMap.merge(value - 1, 1, Integer::sum);
                return value - 1;
            });
        case 3:
            Integer freqVal = freqMap.get(qVal);
            res.add(freqVal == null || freqVal < 1 ? 0 : 1);
        }
    }
    return res;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM