895. Maximum Frequency Stack

Implement FreqStack, a class which simulates the operation of a stack-like data structure.

FreqStack has two functions:

    push(int x), which pushes an integer x onto the stack.
    pop(), which removes and returns the most frequent element in the stack.
        If there is a tie for most frequent element, the element closest to the top of the stack is
        removed and returned.


Example 1:

Input:
["FreqStack","push","push","push","push","push","push","pop","pop","pop","pop"],
[[],[5],[7],[5],[7],[4],[5],[],[],[],[]]
Output: [null,null,null,null,null,null,null,5,7,5,4]
Explanation:
After making six .push operations, the stack is [5,7,5,7,4,5] from bottom to top.  Then:

pop() -> returns 5, as 5 is the most frequent.
The stack becomes [5,7,5,7,4].

pop() -> returns 7, as 5 and 7 is the most frequent, but 7 is closest to the top.
The stack becomes [5,7,5,4].

pop() -> returns 5.
The stack becomes [5,7,4].

pop() -> returns 4.
The stack becomes [5,7].



Note:

    Calls to FreqStack.push(int x) will be such that 0 <= x <= 10^9.
    It is guaranteed that FreqStack.pop() won't be called if the stack has zero elements.
    The total number of FreqStack.push calls will not exceed 10000 in a single test case.
    The total number of FreqStack.pop calls will not exceed 10000 in a single test case.
    The total number of FreqStack.push and FreqStack.pop calls will not exceed 150000 across all test cases.
  • O(N)
    • duplicate at each freqency
    • When increase freqency for an element:
      • do not remove the element in the current stack;
      • keep it and add another copy to the next stack
      • by doing this we can keep the relative adding order of the elements

class FreqStack {
    int high;
    HashMap<Integer, Stack<Integer>> f2k;
    HashMap<Integer, Integer> k2f;
    public FreqStack() {
        f2k = new HashMap<>();
        k2f = new HashMap<>();
        high = 0;
    }

    public void push(int x) {
        int f = k2f.getOrDefault(x, 0);
        k2f.put(x, ++f);
        Stack<Integer> s = f2k.getOrDefault(f, new Stack<>());
        s.push(x);
        f2k.put(f, s);
        high = Math.max(high, f);
    }

    public int pop() {
        int result = f2k.get(high).pop();

        if(k2f.get(result) == 1) k2f.remove(result);
        else k2f.put(result, k2f.get(result)-1);

        if(f2k.get(high).isEmpty()) {
            f2k.remove(high);
            high--;
        }
        return result;
    }
}

  • Pass on first submit
    • similar to LFC & All in One data structure

class FreqStack {
    int timestamp = 0;
    int high = 0;
    HashMap<Integer, PriorityQueue<Node>> f2k = new HashMap<>(); // freqency -> keys
    HashMap<Integer, Node> map = new HashMap<>();
    public FreqStack() {

    }

    public void push(int x) {
        Node n = map.getOrDefault(x, new Node(x, timestamp++));
        if(map.containsKey(x)) n.add(timestamp++);
        map.put(x, n);
        f2k.getOrDefault(n.size()-1, new PriorityQueue<>()).remove(n);
        PriorityQueue<Node> q = f2k.getOrDefault(n.size(), new PriorityQueue<>());
        q.offer(n);
        f2k.put(n.size(), q);
        high = Math.max(high, n.size());
    }

    public int pop() {
        PriorityQueue<Node> q = f2k.get(high);
        Node n = q.poll();
        if(q.isEmpty()) {
            f2k.remove(high);
            high--;
        }
        int result = n.key;

        n.reduce();
        if(n.isEmpty()) map.remove(n.key);
        else {
            PriorityQueue<Node> lowQ = f2k.getOrDefault(n.size(), new PriorityQueue<>());
            lowQ.offer(n);
            f2k.put(n.size(), lowQ);
        }
        return result;
    }

    class Node implements Comparable<Node>{
        int key;
        ArrayList<Integer> times = new ArrayList<>();
        public Node(int k, int t) {
            key = k;
            times.add(t);
        }
        public void add(int t) {
            times.add(t);
        }
        public void reduce() {
            times.remove(times.size()-1);
        }
        public int size() {
            return times.size();
        }
        public boolean isEmpty() {
            return times.isEmpty();
        }
        private int getLatest() {
            return times.get(times.size()-1);
        }
        @Override
        public int compareTo(Node n) {
            return n.getLatest() - this.getLatest();
        }
        public String toString() {
            return "" + key + "->" + times;
        }
    }
}

/**
 * Your FreqStack object will be instantiated and called as such:
 * FreqStack obj = new FreqStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 */