2022.3.16总结+力扣432. 全 O(1) 的数据结构

昨天手机上提交的,时间匆忙,没来得及写,今日补上哦!!

请你设计一个用于存储字符串计数的数据结构,并能够返回计数最小和最大的字符串。

实现 AllOne 类:

AllOne() 初始化数据结构的对象。
inc(String key) 字符串 key 的计数增加 1 。如果数据结构中尚不存在 key ,那么插入计数为 1 的 key 。
dec(String key) 字符串 key 的计数减少 1 。如果 key 的计数在减少后为 0 ,那么需要将这个 key 从数据结构中删除。测试用例保证:在减少计数前,key 存在于数据结构中。
getMaxKey() 返回任意一个计数最大的字符串。如果没有元素存在,返回一个空字符串 “” 。
getMinKey() 返回任意一个计数最小的字符串。如果没有元素存在,返回一个空字符串 “” 。

示例:

输入
[“AllOne”, “inc”, “inc”, “getMaxKey”, “getMinKey”, “inc”, “getMaxKey”, “getMinKey”]
[[], [“hello”], [“hello”], [], [], [“leet”], [], []]
输出
[null, null, null, “hello”, “hello”, null, “hello”, “leet”]

解释
AllOne allOne = new AllOne();
allOne.inc(“hello”);
allOne.inc(“hello”);
allOne.getMaxKey(); // 返回 “hello”
allOne.getMinKey(); // 返回 “hello”
allOne.inc(“leet”);
allOne.getMaxKey(); // 返回 “hello”
allOne.getMinKey(); // 返回 “leet”

提示:

1 <= key.length <= 10
key 由小写英文字母组成
测试用例保证:在每次调用 dec 时,数据结构中总存在 key
最多调用 inc、dec、getMaxKey 和 getMinKey 方法 5 * 104 次

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/all-oone-data-structure
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


这个题目的确很难,读题明白意思就有些头大,不过看例子是不错的想法。让我想到了【146. LRU 缓存机制】,感兴趣的可以看这个面试题

定义一个节点类 Node,除了包含用于实现双向链表的 left 和 right 以外,还包含一个数值类型的变量 cnt, 用于记录该节点存储的是计数次数为多少的元素,以及一个 Set 类型的容器,用于支持 O(1) 插入和删除元素,记作 set。

同时为了快速知道某个字符串属于哪个 Node,我们还需要开一个「哈希表」进行定位(以字符串为哈希表的键,字符串所在 Node 作为值),当定位到字符串对应的 Node 之后则可以利用双向链表的 O(1) 增加/修改/删除。

在双向链表中,起始只有两个哨兵节点 hh和 tt ,当进行若干 inc/dec 操作后的基本形态为:

在这里插入图片描述

对应几个操作:

inc/dec 操作:当对一个字符串 key 进行「增加计数」或「减少计数」时,先在哈希表中看 key 是否存在:

若存在:根据其所属的 Node 的计数 cnt 为多少,并结合当前是「增加计数」还是「减少计数」来决定是找 Node 的「右节点」还是「左节点」,同时检查相邻节点的计数值 cnt 是否为目标值,对应要检查数值是 cnt + 1cnt+1 和 cnt - 1cnt−1:
若相邻节点的 cnt 为目标值:即目标节点存在,将 key 从原 Node 的 set 集合中移除,并添加到目标节点的集合中,更新哈希表;
若相邻节点的 cnt 不是目标值:则需要创建相应的目标节点,并构建双向链表关系,把 key 存入新创建的目标节点,更新哈希表。
若不存在(只能是 inc 操作):查找是否存在 cnt = 1cnt=1 的节点(也就是检查 hh.right 节点的计数值):
如果存在 cnt = 1cnt=1 的目标节点:将 key 添加到目标节点的 set 集合中,更新哈希表;
若不存在 cnt = 1cnt=1 的目标节点:创建相应的节点,并构建双向关系,并构建双向链表关系,把 key 存入新创建的目标节点,更新哈希表。
getMaxKey/getMinKey 操作:分别从 tt.left 和 hh.right 中尝试查找,如果存在非哨兵节点,则从节点的 set 集合中取任意元素进行返回,否则返回空串。

最后,为了确保 getMaxKey/getMinKey 操作能够严格 O(1)O(1),我们在进行 inc/dec 操作时我们需要对一些 set 容量为 00 的节点进行释放,即解除其所在双向链表的关系。

作者:AC_OIer
链接:https://leetcode-cn.com/problems/all-oone-data-structure/solution/by-ac_oier-t26d/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

struct Node {
    string s;
    int v;
    Node *pre, *nxt;
    Node(string s):s(s), v(0), pre(nullptr), nxt(nullptr) {};
};

class AllOne {
    vector<pair<Node*, Node*>> mp1;
    map<string, Node*> mp2;
    Node *head, *tail;
public:
    AllOne():mp1(50002) {
        head = mp1[0].second = new Node("");
        tail = mp1.back().first = new Node("");
        head->nxt = tail;
        tail->pre = head;
    }
    // 将 it 节点插入 tar 节点后面
    void insert(Node *it, Node *tar) {
        it->nxt = tar->nxt;
        it->pre = tar;
        it->nxt->pre = it->pre->nxt = it;
        auto &[a, b] = mp1[it->v];
        if (!a && !b) {
            a = b = it;
        } else {
            a = it;
        }
    }
    // 将 it 节点从双向链表中删除
    void erase(Node *it) {
        it->pre->nxt = it->nxt;
        it->nxt->pre = it->pre;
        auto &[a, b] = mp1[it->v];
        if (a == it && b == it) {
            a = b = nullptr;
        } else if (a == it) {
            a = it->nxt;
        } else if (b == it) {
            b = it->pre;
        }
    }
    void inc(string key) {
        Node *cur = mp2.count(key) ? mp2[key] : new Node(key);
        if (cur->v) {
            erase(cur);
            cur->v++;
            if (mp1[cur->v - 1].second) {
                insert(cur, mp1[cur->v - 1].second);
            } else {
                insert(cur, cur->pre);
            }
        } else {
            mp2[key] = cur;
            cur->v++;
            insert(cur, head);
        }
    }
    
    void dec(string key) {
        Node *cur = mp2[key];
        erase(cur);
        if (cur->v == 1) {
            mp2.extract(key);
            delete cur;
        } else {
            cur->v--;
            if (mp1[cur->v].first) {
                insert(cur, mp1[cur->v].first->pre);
            } else {
                insert(cur, cur->pre);
            }
        }
    }
    
    string getMaxKey() {
        return tail->pre->s;
    }
    
    string getMinKey() {
        return head->nxt->s;
    }
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值