LeetCode打卡day16

194 阅读2分钟

“Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。”

一、题目描述:

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

二、思路分析:

哈希表+双向链表

我们再加入或减少对应字符串的出现次数count时,首先是要知道操作前的count值,因此我们肯定要用到哈希表存储,键值为字符串key

又因为字符串对应的key值是变化的,与之前做的最小栈之类题目输入的固定数值不同,为了维持值变化后任能方便获取最大最小值,我们可以使用双向链表或者最大、最小堆。这里我选择双向链表

创建双向链表类Node,我们希望用它存储字符串和对应的count值,可以通过Node对象方便获取对应的值和字符串。使用node对象组成双向链表,按字符串出现次数升序排列,pre属性指向前一个node对象,next属性指向下一个node对象。

同时进行inc操作时,插入新的node对象是必要的操作,我们编写insert(node2)方法,方便对象node后面插入node2对象。同时dec操作时要删除count为0的值,remove方法也是必要的。

接着我们发现如果只是单纯的插入和删除,不可避免的要判断插入时下一个结点是否为null,删除时前一个结点是否为null,很繁琐。故我们使用root和tail来表示头结点和尾结点。同时我们发现,使用他们还能快捷的知道最大值——tail.pre和最小值——root.next。

node类如下

class Node{
    constructor(key,count){
        this.count=count||0
        this.key=key||''
        this.pre=null
        this.next=null
    }
    insert(node){
        node.pre=this
        node.next=this.next
        this.next.pre=node
        this.next=node
        
    }
    remove(){
        this.pre.next=this.next
        this.next.pre=this.pre
    }
}

接下来我们逐步分析对应四个操作

  1. inc操作
    • 若插入key字符串,不存在在哈希表中,则创建node对象count为1,加入哈希表map,root头结点insert该node对象
    • 哈希表中存在,若只有该节点或者已经按升序排列的链表中该节点的下一个结点next的count值大于或者等于count+1,该节点的node对象count直接加一即可
    • 否则,我们要找到第一个count值大于或者等于count+1的结点next或者尾结点,在next后面insert新的count+1的node对象,调用remove方法删除旧的node对象,同时更新哈希表key对应的node对象
    代码如下
 /** 
 * @param {string} key
 * @return {void}
 */
AllOne.prototype.inc = function(key) {
    let [root,map]=[this.root,this.map]
    if(map.has(key)){
        let node=map.get(key)
        let next=node.next
        if(next===this.tail||next.count>=node.count+1){
            node.count++
        }else{
            while(next.next!==this.tail&&next.next.count<node.count+1){
                next=next.next
            }
            next.insert(new Node(key,node.count+1))
            node.remove()
            map.set(key,next.next)
        }
    }else{
        let node=new Node(key,1)
        map.set(key,node)
        root.insert(node)
    }
};
  1. dec操作
    • 只有该节点时,直接node.count--,若为0删除结点
    • node.count===1,删除该节点
    • 前一个结点pre.count<=node.count-1,直接node.count--,若为0删除结点
    • 否则,node.count值肯定是>=2的,找到第一个小于node.count的结点pre或者头结点,在pre后面insert新的count-1的node对象,调用remove方法删除旧的node对象,同时更新哈希表key对应的node对象 代码如下
AllOne.prototype.dec = function(key) {
    let [root,map]=[this.root,this.map]
    if(map.has(key)){
        let node=map.get(key)
        let pre=node.pre
        if(pre===root||node.count===1||pre.count<=node.count-1){
            node.count--
            if(node.count===0){
                node.remove()
                map.delete(key)
            }
        }else{
            while(pre!==root&&pre.count>node.count-1){
                pre=pre.pre
            }
            pre.insert(new Node(key,--node.count))
            node.remove()
            map.set(key,pre.next)

        }

    }else{
            console.log("未加入就删除")
    }
};
  1. getMaxKey 判断尾结点tail上一个是否为root,是则表示空链表,没有符合的值返回"",否则返回tail.pre.key

  2. getMinKey 判断头结点root下一个是否为tail,是则表示空链表,没有符合的值返回"",否则返回root.next.key

AC 代码

class Node{
    constructor(key,count){
        this.count=count||0
        this.key=key||''
        this.pre=null
        this.next=null
    }
    insert(node){
        node.pre=this
        node.next=this.next
        this.next.pre=node
        this.next=node
        
    }
    remove(){
        this.pre.next=this.next
        this.next.pre=this.pre
    }
}


var AllOne = function() {
    this.map=new Map()
    this.root=new Node()
    this.tail=new Node()
    this.tail.pre=this.root
    this.root.next=this.tail
};

/** 
 * @param {string} key
 * @return {void}
 */
AllOne.prototype.inc = function(key) {
    let [root,map]=[this.root,this.map]
    if(map.has(key)){
        let node=map.get(key)
        let next=node.next
        if(next===this.tail||next.count>=node.count+1){
            node.count++
        }else{
            while(next.next!==this.tail&&next.next.count<node.count+1){
                next=next.next
            }
            next.insert(new Node(key,node.count+1))
            node.remove()
            map.set(key,next.next)
        }
    }else{
        let node=new Node(key,1)
        map.set(key,node)
        root.insert(node)
    }
};

/** 
 * @param {string} key
 * @return {void}
 */
AllOne.prototype.dec = function(key) {
    let [root,map]=[this.root,this.map]
    if(map.has(key)){
        let node=map.get(key)
        let pre=node.pre
        if(pre===root||node.count===1||pre.count<=node.count-1){
            node.count--
            if(node.count===0){
                node.remove()
                map.delete(key)
            }
        }else{
            while(pre!==root&&pre.count>node.count-1){
                pre=pre.pre
            }
            pre.insert(new Node(key,--node.count))
            node.remove()
            map.set(key,pre.next)

        }

    }else{
            console.log("未加入就删除")
    }
};

/**
 * @return {string}
 */
AllOne.prototype.getMaxKey = function() {
    if(this.tail.pre!==this.root)
        return  this.tail.pre.key
    else return ''
};

/**
 * @return {string}
 */
AllOne.prototype.getMinKey = function() {
    return this.root.next!==this.tail?this.root.next.key:''
};

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