【Leetcode】432. All O`one Data Structure

题目地址:

https://leetcode.com/problems/all-oone-data-structure/

要求实现一个key-value形式的数据结构,可以做如下操作:
1、inc(String key),将key对应的count增加 1 1 1,如果该key不存在,则新增该key,并将其count设为 1 1 1
2、dec(String key),将key对应的count减少 1 1 1,题目保证输入的key已经存在。如果该key的count恰好就是 1 1 1,则将其删去(也就是说最后不能存在count小于等于 0 0 0的元素);
3、getMaxKey(),得到count最大的某个key,返回任意一个都可以。如果该数据结构已经空了,则返回空串;
4、getMinKey(),得到count最小的某个key,也是返回任意一个都可以。如果该数据结构已经空了,也返回空串;

思路是HashMap + 双向链表。链表的节点里主要存count和该count总共对应着哪些key(用一个HashSet存储),并且维护相邻count的顺序关系(例如从表头到表尾是count递增的顺序),而HashMap主要存key和Node的对应关系。这样操作 3 3 3 4 4 4就非常简单了,只需看一下表头和表尾Node里任一找一个key出来就行了。其余两个操作可以这样实现:
1、对于inc,如果key不存在,则看一下表头的count是否是 1 1 1,如果链表是空的,或者链表表头的count不是 1 1 1,则new出count是 1 1 1的Node,否则直接取出表头,并把该key加入表头;如果key存在,则找到其对应count的Node,看一下它的next Node是否存在、count是否是key对应的count加 1 1 1,如果不是则new出来,否则取出next Node,把key加进去,同时要看一下原count对应的Node是不是不含任何key了,如果是,则还要把该Node也删掉。无论怎样都要维护HashMap的信息;
2、对于dec,其实和inc是对称的做,只有两点不同,第一,如果该key不存在,则可以什么都不干(当然题目已经保证了key一定存在了),第二,如果dec到 0 0 0了,需要从HashMap里将key删掉。

代码如下:

class AllOne {
 public:
  struct Node {
    Node *prev, *next;
    unordered_set<string> keys;
    int cnt;

    Node(const string& key, int cnt) {
      keys.insert(key);
      this->cnt = cnt;
    }
  };

  struct LinkedList {
    Node *head, *tail;
    int sz;

    LinkedList() {
      head = new Node("", 0);
      tail = new Node("", 0);
      head->next = tail;
      tail->prev = head;
    }

    void add_first(Node* node) { add_after(head, node); }

    void add_after(Node* cur, Node* node) {
      node->prev = cur;
      node->next = cur->next;
      cur->next = node;
      node->next->prev = node;
      sz++;
    }

    void remove(Node* node) {
      node->prev->next = node->next;
      node->next->prev = node->prev;
      sz--;
    }

    bool empty() { return sz == 0; }
  };

  LinkedList list;
  unordered_map<string, Node*> mp;

  AllOne() {}

  void inc(string key) {
    // 如果key之前没插入过,则该key的频率是1,首先看一下
    // 链表里有没有频率为1的节点,有则直接插入key;否则将
    // 节点new出来插入key
    if (!mp.count(key)) {
      if (list.empty() || list.head->next->cnt != 1) {
        auto node = new Node(key, 1);
        list.add_first(node);
        mp[key] = node;
      } else {
        list.head->next->keys.insert(key);
        mp[key] = list.head->next;
      }
    } else {
      // key之前插入过,则其频率要增加1。看一下其频率 + 1的
      // 节点存不存在,存在则直接插,否则将节点new出来再插
      auto node = mp[key];
      node->keys.erase(key);
      if (node->next == list.tail || node->next->cnt != node->cnt + 1)
        list.add_after(node, new Node(key, node->cnt + 1));
      mp[key] = node->next;
      node->next->keys.insert(key);
      if (node->keys.empty()) list.remove(node);
    }
  }

  void dec(string key) {
    if (!mp.count(key)) return;
    auto node = mp[key];
    node->keys.erase(key);
    if (node->cnt == 1)
      mp.erase(key);
    else {
      if (node->prev == list.head || node->prev->cnt != node->cnt - 1)
        list.add_after(node->prev, new Node(key, node->cnt - 1));
      node->prev->keys.insert(key);
      mp[key] = node->prev;
    }
    if (node->keys.empty()) list.remove(node);
  }

  string getMaxKey() {
    if (list.empty()) return "";
    return *list.tail->prev->keys.begin();
  }

  string getMinKey() {
    if (list.empty()) return "";
    return *list.head->next->keys.begin();
  }
};

所有操作时间复杂度 O ( l ) O(l) O(l) l l l是输入的key的长度,注意这里不是 O ( 1 ) O(1) O(1),而是key的长度,因为在HashMap里找key的复杂度是 O ( l ) O(l) O(l)),空间 O ( n l ) O(nl) O(nl) n n n是key的个数, l l l是最长key的长度。注意,尽管这里操作时间复杂度不是 O ( 1 ) O(1) O(1),但是也不可能更优了,因为遍历key字符串是必须做的,不可能降到 O ( 1 ) O(1) O(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值