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

本文介绍了如何设计一个名为AllOne的数据结构,该结构支持字符串计数的增减操作,并能在O(1)的时间复杂度内完成getMaxKey和getMinKey方法,实现对最大和最小计数字符串的检索。核心思想是利用双向链表存储计数及其对应的字符串集合,并通过哈希表快速定位节点。
摘要由CSDN通过智能技术生成

题目来源:https://leetcode-cn.com/problems/all-oone-data-structure/

大致题意:
设计一个类 AllOne,包含以下方法

  • 无参构造器
  • inc(String key) key 对应的字符串计数 +1
  • dec(String key) key 对应的字符串计数 -1,若为 0 则删除该字符串(题目保证 -1 时一定有对应的字符串)
  • getMaxKey() 返回任意一个计数最大的字符串。如果没有元素存在,返回一个空字符串 “”
  • getMinKey() 返回任意一个计数最小的字符串。如果没有元素存在,返回一个空字符串 “”

要求所有操作的时间复杂度为 O(1)

思路

使用一个双向链表存计数以及对应的字符串可以集合,再用一个哈希表标记字符串所在的链表节点

这样每次调用方法 inc(key) 时,按照如下规则:

  • 先判断哈希表中是否有该 key 对应的节点,若有,则获取对应计数,然后将 key 放入计数 +1 对应的节点中(若该节点没有则创建),并在原节点的字符串集合中删除 key(若删除后集合为空,则移除该节点);若没有则插入计数 1 对应的链表节点(若该节点没有则先创建)中。最后在哈希表中更新 key 对应节点

调用方法 dec(key) 时,按照如下规则:

  • 先获取对应计数,然后判断计数是否为 1,若为 1 直接从哈希表删除 key 对应的键值对;若不为 1,将 key 放入计数 -1 对应的节点中(若该节点没有则创建)
  • 之后在原节点的字符串集合中删除 key(若删除后集合为空,则移除该节点)

调用方法 getMaxKey() 直接返回最后一个节点

调用方法 getMinKey() 直接返回第一个节点

public class AllOne {
    Map<String, Node> map;
    Node root;  // 根节点
    public AllOne() {
        root = new Node();
        root.next = root;
        root.pre = root;
        map = new HashMap<>();
    }

    public void inc(String key) {
        // 若哈希表中有当前字符串,获取所在节点和已有次数
        if (map.containsKey(key)) {
            Node node = map.get(key);
            int count = node.count + 1;
            // 更新 key 所在节点
            updateInc(key, node, count);
            // 从原节点的字符串集合中删去 key
            node.keys.remove(key);
            // 若原节点对应的字符串为空,删除节点
            if (node.keys.isEmpty()) {
                node.remove();
            }
        } else {    // 哈希表中没有该字符串,插入
            updateInc(key, root, 1);
        }
    }

    public void dec(String key) {
        // 获取对应节点
        Node node = map.get(key);
        int count = node.count - 1;
        // 更新后计数为 0,从哈希表中删除
        if (count == 0) {
            map.remove(key);
        } else {    // 否则更新 key 所在节点
            updateDes(key, node, count);
        }
        // 从原节点中删除 key
        node.keys.remove(key);
        if (node.keys.isEmpty()) {
            node.remove();
        }
    }

    public String getMaxKey() {
        return root.pre == root ? "" : root.pre.keys.iterator().next();
    }

    public String getMinKey() {
        return root.next == root ? "" : root.next.keys.iterator().next();
    }

    // 更新增加到 count 次的字符串 key 所在的节点,node 为原节点(若之前没在链表中,node 为根节点)
    public void updateInc(String key, Node node, int count) {
        if (node.next == root || node.next.count > count) {
            map.put(key, node.insert(new Node(count, key)));
        } else {
            node.next.keys.add(key);
            map.put(key, node.next);
        }
    }

    // 更新减少到 count 次的字符串 key 所在节点,node 为原节点
    public void updateDes(String key, Node node, int count) {
        if (node.pre == root || node.pre.count < count) {
            map.put(key, node.pre.insert(new Node(count, key)));
        } else {
            node.pre.keys.add(key);
            map.put(key, node.pre);
        }
    }

    class Node {
        int count;
        Set<String> keys;
        Node pre;
        Node next;

        public Node() {

        }

        public Node(int count, String key) {
            this.count = count;
            keys = new HashSet<>();
            keys.add(key);
        }

        // 在当前节点后插入一个节点
        public Node insert(Node node) {
            node.pre = this;
            node.next = this.next;
            node.pre.next = node;
            node.next.pre = node;
            return node;
        }

        // 删除当前节点
        public void remove() {
            this.pre.next = this.next;
            this.next.pre = this.pre;
        }

    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值