All Downloads are FREE. Search and download functionalities are using the official Maven repository.

g0401_0500.s0432_all_oone_data_structure.AllOne Maven / Gradle / Ivy

There is a newer version: 1.38
Show newest version
package g0401_0500.s0432_all_oone_data_structure;

// #Hard #Hash_Table #Design #Linked_List #Doubly_Linked_List
// #2022_07_16_Time_101_ms_(52.51%)_Space_98_MB_(63.00%)

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 432 - All O\`one Data Structure\.
 *
 * Hard
 *
 * Design a data structure to store the strings' count with the ability to return the strings with minimum and maximum counts.
 *
 * Implement the `AllOne` class:
 *
 * *   `AllOne()` Initializes the object of the data structure.
 * *   `inc(String key)` Increments the count of the string `key` by `1`. If `key` does not exist in the data structure, insert it with count `1`.
 * *   `dec(String key)` Decrements the count of the string `key` by `1`. If the count of `key` is `0` after the decrement, remove it from the data structure. It is guaranteed that `key` exists in the data structure before the decrement.
 * *   `getMaxKey()` Returns one of the keys with the maximal count. If no element exists, return an empty string `""`.
 * *   `getMinKey()` Returns one of the keys with the minimum count. If no element exists, return an empty string `""`.
 *
 * **Example 1:**
 *
 * **Input**
 *
 *     ["AllOne", "inc", "inc", "getMaxKey", "getMinKey", "inc", "getMaxKey", "getMinKey"]
 *     [ [], ["hello"], ["hello"], [], [], ["leet"], [], []]
 *
 * **Output:** [null, null, null, "hello", "hello", null, "hello", "leet"]
 *
 * **Explanation:**
 *
 *     AllOne allOne = new AllOne();
 *     allOne.inc("hello");
 *     allOne.inc("hello");
 *     allOne.getMaxKey(); // return "hello"
 *     allOne.getMinKey(); // return "hello"
 *     allOne.inc("leet");
 *     allOne.getMaxKey(); // return "hello"
 *     allOne.getMinKey(); // return "leet" 
 *
 * **Constraints:**
 *
 * *   `1 <= key.length <= 10`
 * *   `key` consists of lowercase English letters.
 * *   It is guaranteed that for each call to `dec`, `key` is existing in the data structure.
 * *   At most 5 * 104 calls will be made to `inc`, `dec`, `getMaxKey`, and `getMinKey`.
**/
public class AllOne {
    // maintain a doubly linked list of Buckets
    private Bucket head;
    private Bucket tail;
    // for accessing a specific Bucket among the Bucket list in O(1) time
    private Map countBucketMap;
    // keep track of count of keys
    private Map keyCountMap;

    // each Bucket contains all the keys with the same count
    private static class Bucket {
        int count;
        Set keySet;
        Bucket next;
        Bucket pre;

        public Bucket(int cnt) {
            count = cnt;
            keySet = new HashSet<>();
        }
    }

    /* Initialize your data structure here. */
    public AllOne() {
        head = new Bucket(Integer.MIN_VALUE);
        tail = new Bucket(Integer.MAX_VALUE);
        head.next = tail;
        tail.pre = head;
        countBucketMap = new HashMap<>();
        keyCountMap = new HashMap<>();
    }

    /* Inserts a new key  with value 1. Or increments an existing key by 1. */
    public void inc(String key) {
        if (keyCountMap.containsKey(key)) {
            changeKey(key, 1);
        } else {
            keyCountMap.put(key, 1);
            if (head.next.count != 1) {
                addBucketAfter(new Bucket(1), head);
            }
            head.next.keySet.add(key);
            countBucketMap.put(1, head.next);
        }
    }

    /* Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */
    public void dec(String key) {
        if (keyCountMap.containsKey(key)) {
            int count = keyCountMap.get(key);
            if (count == 1) {
                keyCountMap.remove(key);
                removeKeyFromBucket(countBucketMap.get(count), key);
            } else {
                changeKey(key, -1);
            }
        }
    }

    /* Returns one of the keys with maximal value. */
    public String getMaxKey() {
        return tail.pre == head ? "" : (String) tail.pre.keySet.iterator().next();
    }

    /* Returns one of the keys with Minimal value. */
    public String getMinKey() {
        return head.next == tail ? "" : (String) head.next.keySet.iterator().next();
    }

    // helper function to make change on given key according to offset
    private void changeKey(String key, int offset) {
        int count = keyCountMap.get(key);
        keyCountMap.put(key, count + offset);
        Bucket curBucket = countBucketMap.get(count);
        Bucket newBucket;
        if (countBucketMap.containsKey(count + offset)) {
            // target Bucket already exists
            newBucket = countBucketMap.get(count + offset);
        } else {
            // add new Bucket
            newBucket = new Bucket(count + offset);
            countBucketMap.put(count + offset, newBucket);
            addBucketAfter(newBucket, offset == 1 ? curBucket : curBucket.pre);
        }
        newBucket.keySet.add(key);
        removeKeyFromBucket(curBucket, key);
    }

    private void removeKeyFromBucket(Bucket bucket, String key) {
        bucket.keySet.remove(key);
        if (bucket.keySet.isEmpty()) {
            removeBucketFromList(bucket);
            countBucketMap.remove(bucket.count);
        }
    }

    private void removeBucketFromList(Bucket bucket) {
        bucket.pre.next = bucket.next;
        bucket.next.pre = bucket.pre;
        bucket.next = null;
        bucket.pre = null;
    }

    // add newBucket after preBucket
    private void addBucketAfter(Bucket newBucket, Bucket preBucket) {
        newBucket.pre = preBucket;
        newBucket.next = preBucket.next;
        preBucket.next.pre = newBucket;
        preBucket.next = newBucket;
    }
}

/*
 * Your AllOne object will be instantiated and called as such:
 * AllOne obj = new AllOne();
 * obj.inc(key);
 * obj.dec(key);
 * String param_3 = obj.getMaxKey();
 * String param_4 = obj.getMinKey();
 */




© 2015 - 2025 Weber Informatics LLC | Privacy Policy