Skip to main content

All O`one Data Structure

Problem Statement

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 "". Note that each function must run in O(1) average time complexity.

Leetcode Link

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.

Code

Python
class Block(object):
def __init__(self, val=0):
self.val = val
self.keys = set()
self.before = None
self.after = None

def remove(self):
self.before.after = self.after
self.after.before = self.before
self.before, self.after = None, None

def insert_after(self, new_block):
old_after = self.after
self.after = new_block
new_block.before = self
new_block.after = old_after
old_after.before = new_block


class AllOne(object):
def __init__(self):
self.begin = Block() # sentinel
self.end = Block() # sentinel
self.begin.after = self.end
self.end.before = self.begin
self.mapping = {} # key to block

def inc(self, key):
if not key in self.mapping: # find current block and remove key
current_block = self.begin
else:
current_block = self.mapping[key]
current_block.keys.remove(key)

if current_block.val + 1 != current_block.after.val: # insert new block
new_block = Block(current_block.val + 1)
current_block.insert_after(new_block)
else:
new_block = current_block.after

new_block.keys.add(key) # update new_block
self.mapping[key] = new_block # ... and mapping of key to new_block

if not current_block.keys and current_block.val != 0: # delete current block if not seninel
current_block.remove()

def dec(self, key):
if not key in self.mapping:
return

current_block = self.mapping[key]
del self.mapping[key] # could use self.mapping.pop(key)
current_block.keys.remove(key)

if current_block.val != 1:
if current_block.val - 1 != current_block.before.val: # insert new block
new_block = Block(current_block.val - 1)
current_block.before.insert_after(new_block)
else:
new_block = current_block.before
new_block.keys.add(key)
self.mapping[key] = new_block

if not current_block.keys: # delete current block
current_block.remove()

def getMaxKey(self):
if self.end.before.val == 0:
return ""
key = self.end.before.keys.pop() # pop and add back to get arbitrary (but not random) element
self.end.before.keys.add(key)
return key

def getMinKey(self):
if self.begin.after.val == 0:
return ""
key = self.begin.after.keys.pop()
self.begin.after.keys.add(key)
return key