[英雄星球六月集训LeetCode解题日报] 第13日 双向链表

[英雄星球六月集训LeetCode解题日报] 第13日 双向链表

一、 432. 全 O(1) 的数据结构

链接: 432. 全 O(1) 的数据结构

1. 题目描述

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

实现 AllOne 类:

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

2. 思路分析

这种模拟题就随便水水,过了之后再看看题解学习别人的奇思妙想。
我的实现是双向链表+两个哈希表:

  • 一个哈希表储存单词次数; 另一个储存单词在链表的哪个节点里,否则每次新加或删除已存在的key都要去链表遍历。

  • 双向链表按词频顺序储存单词。

  • 双向链表增加head和tail虚拟节点,便于编码。

  • 初始化让head指向tail,同时当head指向tail时表示链表为空。

  • 那么getMIN和getMAX非常好写,就是返回头尾节点即可。O(1)

  • 然后实现在列表里添加和删除节点的方法。

  • 增加单词时,如果是新词,词频必是1,创建一个新节点,插到链表头即可O(1);否则摘除本节点a,向后寻找O(n),找到第一个词频大于它的节点b,把a插到b前边。

  • 删除单词时,如果词频为0直接删除O(1);否则摘除本节点a,向前寻找 O(n) 到第一个词频小于它的节点b,把a插到b后边。

  • 过辣!☀️
    看了题解,链表维护的是词频,每个节点有一个set储存当前词频的单词,这样向后寻找必只需要跳一次,才能做到全O(1).✈
    后来按照题解实现了一次,又用sortedlist写了个 *O(lgn)*的,还是库函数厉害。

  • 在这里插入图片描述

3. 代码实现

双向链表O(n)

class Double_list_node:
    def __init__(self,val='',pre=None,next=None):
        self.val,self.pre,self.next = val,pre,next

class AllOne:
    def __init__(self):
        self.cnt = defaultdict(int)
        self.cursor = defaultdict(None)
        self.head = Double_list_node()
        self.tail = Double_list_node()
        self.head.next = self.tail
        self.tail.pre = self.head

    def delete_node(self,cur):
        cur.pre.next = cur.next
        cur.next.pre = cur.pre
    def add_node_back(self,where,target):
        target.pre = where
        target.next = where.next
        target.pre.next = target
        target.next.pre = target
    def inc(self, key: str) -> None:
        cnt = self.cnt
        tail = self.tail
        cnt[key] += 1
        this_cnt = cnt[key]
        if this_cnt == 1:  # 新来的,创建节点,填到最前边即可
            node = Double_list_node(key)
            self.add_node_back(self.head,node)
            self.cursor[key] = node
        else:  # 不是新来的,本节点摘出来,向后遍历到第一个计数大于它的位置,插到这个位置前边。
            node = self.cursor[key]
            cur  = node.next
            self.delete_node(node)
            while cur != tail and cnt[cur.val] < this_cnt:
                cur = cur.next
            self.add_node_back(cur.pre,node)


    def dec(self, key: str) -> None:
        cnt = self.cnt
        cursor = self.cursor
        head = self.head
        cnt[key] -= 1
        this_cnt = cnt[key]
        node = cursor[key]
        if this_cnt == 0:
            self.delete_node(node)
            del cnt[key]
            del cursor[key]
        else:
            cur = node.pre
            self.delete_node(node)
            while cur != head and cnt[cur.val] > this_cnt:
                cur = cur.pre
            self.add_node_back(cur,node)


    def getMaxKey(self) -> str:
        if self.head.next == self.tail:
            return ''
        return self.tail.pre.val


    def getMinKey(self) -> str:
        if self.head.next == self.tail:
            return ''
        return self.head.next.val

双向链表O(1)

class Double_list_node:
    def __init__(self,count=0,s = set(),pre=None,next=None):
        self.count,self.s,self.pre,self.next = count,s,pre,next

class AllOne:
    def __init__(self):
        self.cursor = defaultdict(None)
        self.head = Double_list_node()
        self.tail = Double_list_node()
        self.head.next = self.tail
        self.tail.pre = self.head

    def delete_node(self,cur):
        cur.pre.next = cur.next
        cur.next.pre = cur.pre
    def add_node_back(self,where,target):
        target.pre = where
        target.next = where.next
        target.pre.next = target
        target.next.pre = target
    def inc(self, key: str) -> None:
        cursor = self.cursor
        head = self.head
        tail = self.tail
        node = cursor.get(key)
        if not node:  # key不存在,插到词频1节点里
            if head.next == tail or head.next.count >1:  # 没有节点,创建一个词频1的节点。
                self.add_node_back(head,Double_list_node(1,{key}))                
            else:
                head.next.s.add(key)
            cursor[key] = head.next
        else:  # key存在,向右移动一位,如果没了或词频不止大1则创建
            nxt = node.next
            node.s.remove(key)
            if nxt == tail or nxt.count > node.count + 1:
                self.add_node_back(node,Double_list_node(node.count + 1,{key}))                
            else:
                nxt.s.add(key) 
            if not node.s:
                self.delete_node(node)       
            cursor[key] = node.next        

    def dec(self, key: str) -> None:
        cursor = self.cursor
        head = self.head
        tail = self.tail
        node = cursor.get(key)
        # 题目保证key存在,就不判断node了
        node.s.remove(key)  
        if node.count == 1:  # 如果没了要删掉指针
             del cursor[key]
        else:  # 向前移动一位,如果没了或词频不止小一则创建
            pre = node.pre
            if pre == head or pre.count < node.count -1:
                self.add_node_back(pre,Double_list_node(node.count - 1,{key}))
            else:
                pre.s.add(key)
            cursor[key] = node.pre
        if not node.s:
            self.delete_node(node)


    def getMaxKey(self) -> str:
        if self.head.next == self.tail:
            return ''
        for s in  self.tail.pre.s:
            return s


    def getMinKey(self) -> str:
        if self.head.next == self.tail:
            return ''
        for s in  self.head.next.s:
            return s

sortedlistO(lgn)

class Double_list_node:
    def __init__(self,count=0,s = set(),pre=None,next=None):
        self.count,self.s,self.pre,self.next = count,s,pre,next

class AllOne:
    def __init__(self):
        from sortedcontainers import SortedList
        self.keys = SortedList()
        self.cnt = defaultdict(int)

    def inc(self, key: str) -> None:
        keys = self.keys 
        cnt = self.cnt   
        cnt[key] += 1
        if cnt[key] == 1:
            keys.add((cnt[key],key))
        else:
            keys.remove((cnt[key]-1,key))
            keys.add((cnt[key],key))

    def dec(self, key: str) -> None:
        keys = self.keys 
        cnt = self.cnt   
        keys.remove((cnt[key],key))
        cnt[key] -= 1
        if cnt[key] > 0:            
            keys.add((cnt[key],key))
        else:
            del cnt[key]


    def getMaxKey(self) -> str:
        if not self.keys:
            return ''
        return self.keys[-1][1]


    def getMinKey(self) -> str:
        if not self.keys:
            return ''
        return self.keys[0][1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值