leetcode - 432. All O`one Data Structure

发布于:2025-02-11 ⋅ 阅读:(30) ⋅ 点赞:(0)

Description

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.

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 * 10^4 calls will be made to inc, dec, getMaxKey, and getMinKey.

Solution

A very good question, but 35min to solve it might be a little bit tough.

Use a double linked list to store the frequency and the string keys. And a reversed index hashmap to store the string key with the frequency.

Previously I used a set in the node to store the key, but I will need to transform the set to a list for getMaxKey and getMinKey. So I used a list instead and it’s even more complicated. I saw in the solution that we could use next(iter(set)) to get a random one in o ( 1 ) o(1) o(1) time, so I would use that in the set solution. But I think using a list is also a good solution.

Code

With a set

class LinkedNode:
    def __init__(self, key: int) -> None:
        self.key = key
        # {string_key1, string_key2, ...}
        self.values = set()
        self.prev = None
        self.next = None

class AllOne:

    def __init__(self):
        # {string_key: node}
        self.hashmap = {}
        self.max_node = LinkedNode(0)
        self.min_node = LinkedNode(0)
        self.max_node.next = self.min_node
        self.min_node.prev = self.max_node

    def _insert_before(self, key: str, node: LinkedNode) -> None:
        # if the previous node is max_node, create a new node
        if node.prev == self.max_node or node.prev.key != node.key + 1:
            new_node = LinkedNode(node.key + 1)
            prev_node = node.prev
            prev_node.next = new_node
            new_node.prev = prev_node
            node.prev = new_node
            new_node.next = node
        # else put it in
        node.values.remove(key)
        node.prev.values.add(key)
        # update the self.hashmap
        self.hashmap[key] = node.prev
    
    def _insert_after(self, key: str, node: LinkedNode) -> None:
        if node.next.key != node.key - 1:
            new_node = LinkedNode(node.key - 1)
            next_node = node.next
            new_node.prev = node
            node.next = new_node
            next_node.prev = new_node
            new_node.next = next_node
        node.values.remove(key)
        node.next.values.add(key)
        self.hashmap[key] = node.next
    
    def _delete_node(self, node: LinkedNode) -> None:
        prev_node, next_node = node.prev, node.next
        prev_node.next = next_node
        next_node.prev = prev_node
        node.prev = None
        node.next = None

    def _print_nodes(self) -> None:
        node = self.max_node.next
        while node != self.min_node:
            print(node.key)
            print(node.values)
            node = node.next

    def inc(self, key: str) -> None:
        if key not in self.hashmap:
            self.min_node.values.add(key)
            self.hashmap[key] = self.min_node
        node = self.hashmap[key]
        self._insert_before(key, node)
        # delete possible empty nodes
        if node != self.min_node and len(node.values) == 0:
            self._delete_node(node)
        # self._print_nodes()

    def dec(self, key: str) -> None:
        # move the string to the next node
        node = self.hashmap[key]
        self._insert_after(key, node)
        # if next node is min_node, delete it
        if node.next == self.min_node:
            self.min_node.values.clear()
            self.hashmap.pop(key)
        # delete possible empty nodes
        if node != self.min_node and len(node.values) == 0:
            self._delete_node(node)
        # self._print_nodes()

    def getMaxKey(self) -> str:
        if len(self.max_node.next.values) == 0:
            return ''
        else:
            return next(iter(self.max_node.next.values))

    def getMinKey(self) -> str:
        if len(self.min_node.prev.values) == 0:
            return ''
        else:
            return next(iter(self.min_node.prev.values))
        


# Your AllOne object will be instantiated and called as such:
# obj = AllOne()
# obj.inc(key)
# obj.dec(key)
# param_3 = obj.getMaxKey()
# param_4 = obj.getMinKey()

With a list

class LinkedNode:
    def __init__(self, key: int) -> None:
        self.key = key
        # [string_key1, string_key2, ...]
        self.values = []
        self.prev = None
        self.next = None

class AllOne:

    def __init__(self):
        # {string_key: [node, index]}
        self.hashmap = {}
        self.max_node = LinkedNode(0)
        self.min_node = LinkedNode(0)
        self.max_node.next = self.min_node
        self.min_node.prev = self.max_node

    def _insert_before(self, key: str, node: LinkedNode) -> None:
        # if the previous node is max_node, create a new node
        if node.prev == self.max_node or node.prev.key != node.key + 1:
            new_node = LinkedNode(node.key + 1)
            prev_node = node.prev
            prev_node.next = new_node
            new_node.prev = prev_node
            node.prev = new_node
            new_node.next = node
        # else put it in
        cur_key_index, last_key = self.hashmap[key][1], node.values[-1]
        # swap
        node.values[cur_key_index], node.values[-1] = last_key, key
        # update the last key's index
        self.hashmap[last_key][1] = cur_key_index
        node.values.pop()
        # add the key to new node
        node.prev.values.append(key)
        # update the self.hashmap
        self.hashmap[key] = [node.prev, len(node.prev.values) - 1]
    
    def _insert_after(self, key: str, node: LinkedNode) -> None:
        if node.next.key != node.key - 1:
            new_node = LinkedNode(node.key - 1)
            next_node = node.next
            new_node.prev = node
            node.next = new_node
            next_node.prev = new_node
            new_node.next = next_node
        # swap
        cur_key_index, last_key = self.hashmap[key][1], node.values[-1]
        node.values[cur_key_index], node.values[-1] = last_key, key
        node.values.pop()
        # update the last_key's index
        self.hashmap[last_key][1] = cur_key_index
        node.next.values.append(key)
        self.hashmap[key] = [node.next, len(node.next.values) - 1]
    
    def _delete_node(self, node: LinkedNode) -> None:
        prev_node, next_node = node.prev, node.next
        prev_node.next = next_node
        next_node.prev = prev_node
        node.prev = None
        node.next = None

    def inc(self, key: str) -> None:
        if key not in self.hashmap:
            self.min_node.values.append(key)
            self.hashmap[key] = [self.min_node, len(self.min_node.values) - 1]
        node = self.hashmap[key][0]
        self._insert_before(key, node)
        # delete possible empty nodes
        if node != self.min_node and len(node.values) == 0:
            self._delete_node(node)

    def dec(self, key: str) -> None:
        # move the string to the next node
        node = self.hashmap[key][0]
        self._insert_after(key, node)
        # if next node is min_node, delete it
        if node.next == self.min_node:
            self.min_node.values.clear()
            self.hashmap.pop(key)
        # delete possible empty nodes
        if node != self.min_node and len(node.values) == 0:
            self._delete_node(node)

    def getMaxKey(self) -> str:
        if len(self.max_node.next.values) == 0:
            return ''
        else:
            return self.max_node.next.values[0]

    def getMinKey(self) -> str:
        if len(self.min_node.prev.values) == 0:
            return ''
        else:
            return self.min_node.prev.values[0]
        


# Your AllOne object will be instantiated and called as such:
# obj = AllOne()
# obj.inc(key)
# obj.dec(key)
# param_3 = obj.getMaxKey()
# param_4 = obj.getMinKey()

网站公告

今日签到

点亮在社区的每一天
去签到