【日常系列】LeetCode《20·数据结构设计》

news2024/12/23 18:29:26

数据规模->时间复杂度

<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(logn),o(1)

内容

在这里插入图片描述

lc 155 【剑指 30】【top100】:最小栈
https://leetcode.cn/problems/min-stack/
提示:
-2^31 <= val <= 2^31 - 1
pop、top 和 getMin 操作总是在 非空栈 上调用
push, pop, top, and getMin最多被调用 3 * 104 次
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#方案一:两个栈(辅助栈)
class MinStack:

    def __init__(self):
        self.datastack=[]
        self.minstack=[]

    def push(self, val: int) -> None:
        self.datastack.append(val)
        #key:<=
        if not self.minstack or val<=self.minstack[-1]:
            self.minstack.append(val) 

    def pop(self) -> None:
        top=self.datastack.pop()
        #key
        if top==self.minstack[-1]:
            self.minstack.pop()

    def top(self) -> int:
        return self.datastack[-1]


    def getMin(self) -> int:
        return self.minstack[-1]

# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(val)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()

#方案二:一个栈-存储节点
class Node:
    def __init__(self,val=0,minn=0):
        self.val=val
        self.min=minn

class MinStack:

    def __init__(self):
        self.datastack=[]

    def push(self, val: int) -> None:
        #key:
        newnode=Node()
        newnode.val=val
        if not self.datastack:
            newnode.min=val
        else:
            newnode.min=min(self.datastack[-1].min,val)
        #
        self.datastack.append(newnode)
        
    def pop(self) -> None:
        self.datastack.pop()


    def top(self) -> int:
        return self.datastack[-1].val
    def getMin(self) -> int:
        return self.datastack[-1].min
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(val)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()

#方案三:自定义栈-链表结构
class ListNode:
    def __init__(self,val=0,minn=0,next=None):
        self.val=val
        self.min=minn
        self.next=next

class MinStack:

    def __init__(self):
        self.dummynode=ListNode()

    def push(self, val: int) -> None:
        #key:
        newnode=ListNode()
        newnode.val=val
        if not self.dummynode.next:
            newnode.min=val
        else:
            newnode.min=min(self.dummynode.next.min,val)
        #key
        newnode.next=self.dummynode.next
        self.dummynode.next=newnode
        
    def pop(self) -> None:
        firstnode=self.dummynode.next
        if firstnode:
	        self.dummynode.next=firstnode.next
	        firstnode.next=None

    def top(self) -> int:
        return self.dummynode.next.val
        
    def getMin(self) -> int:
        return self.dummynode.next.min
       
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(val)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()

lc 225 :用队列实现栈
https://leetcode.cn/problems/implement-stack-using-queues/
注意:
你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
提示:
1 <= x <= 9
最多调用100 次 push、pop、top 和 empty
每次调用 pop 和 top 都保证栈不为空
进阶:
你能否仅用一个队列来实现栈。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#方案一:两个队列(使用push的调用次数多)
class MyStack:

    def __init__(self):
        self.queue1=deque()
        self.queue2=deque()
        

    def push(self, x: int) -> None:
        self.queue1.append(x)

	#o(n)
    def pop(self) -> int:
        #key-key-key:while
        if self.queue1:
            while(self.queue1):
                x=self.queue1.popleft()
                if not self.queue1:
                    return x
                self.queue2.append(x)#key位置(最后无head元素)
        #
        elif self.queue2:
            while(self.queue2):
                x=self.queue2.popleft()
                if not self.queue2:
                    return x
                self.queue1.append(x)#key位置(最后无head元素)

	#o(n)
    def top(self) -> int:
        if self.queue1:
            while(self.queue1):
                x=self.queue1.popleft()
                self.queue2.append(x)#key位置(最后有head元素)
                if not self.queue1:
                    return x           
        #
        elif self.queue2:
            while(self.queue2):
                x=self.queue2.popleft()
                self.queue1.append(x)#key位置(最后有head元素)
                if not self.queue2:
                    return x
            
    def empty(self) -> bool:
        return len(self.queue1)==0 and len(self.queue2)==0

# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

#方案二:两个队列(优化:适用pop与top的调用次数多)
class MyStack:

    def __init__(self):
        self.queue1=deque()
        self.queue2=deque()
        
    def push(self, x: int) -> None:
        #key-key-key:while
        self.queue2.append(x)
        while(self.queue1):
            self.queue2.append(self.queue1.popleft())
        self.queue1,self.queue2=self.queue2,self.queue1

    def pop(self) -> int:
        return self.queue1.popleft()
                
    def top(self) -> int:
        return self.queue1[0]
                      
    def empty(self) -> bool:
        return not self.queue1 and not self.queue2

# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

#方案三:一个队列
class MyStack:

    def __init__(self):
        self.queue1=deque()   

    def push(self, x: int) -> None:
        #key-key-key:123->321
        size=len(self.queue1)
        self.queue1.append(x)
        for i in range(size):
            self.queue1.append(self.queue1.popleft())

    def pop(self) -> int:
        return self.queue1.popleft()
             
    def top(self) -> int:
        return self.queue1[0]    
            
    def empty(self) -> bool:
        return not self.queue1 

# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

【剑指 09】: 用两个栈实现队列
https://leetcode.cn/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/
提示:
1 <= values <= 10000
最多会对 appendTail、deleteHead 进行 10000 次调用

#方案一:
class CQueue:

    def __init__(self):
        self.stack1=[]
        self.stack2=[]

    def appendTail(self, value: int) -> None:
        while self.stack2:
            self.stack1.append(self.stack2.pop())
        self.stack1.append(value)

    def deleteHead(self) -> int:
        while self.stack1:
            self.stack2.append(self.stack1.pop())
        if not self.stack2:
            return -1
        return self.stack2.pop()

# Your CQueue object will be instantiated and called as such:
# obj = CQueue()
# obj.appendTail(value)
# param_2 = obj.deleteHead()

#方案二:优化
class CQueue:

    def __init__(self):
        self.stack1=[]
        self.stack2=[]

    def appendTail(self, value: int) -> None:
        while self.stack2:
            self.stack1.append(self.stack2.pop())
        self.stack1.append(value)

    def deleteHead(self) -> int:
        #key-key-key:stack2已经把stack1的前入元素变成前出元素
        if not self.stack2:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
        #
        if not self.stack2:
            return -1
        return self.stack2.pop()

# Your CQueue object will be instantiated and called as such:
# obj = CQueue()
# obj.appendTail(value)
# param_2 = obj.deleteHead()

lc 622 :设计循环队列
提示:
-2^31 <= val <= 2^31 - 1
最多调用 insert、remove 和 getRandom 函数 2 * 105 次
在调用 getRandom 方法时,数据结构中 至少存在一个 元素。
注意:你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1)
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

class MyCircularQueue:

    def __init__(self, k: int):
        self.k=k
        self.data=[None]*(k+1)#k=2,5-None-1为满
        self.head=0
        self.tail=0#队尾元素下一位

    def enQueue(self, value: int) -> bool:
        #
        if self.isFull():return False
        #
        self.data[self.tail]=value
        self.tail=(self.tail+1)%(self.k+1)
        return True

    def deQueue(self) -> bool:
        #
        if self.isEmpty():return False
        #
        self.data[self.head]=None   #可以略去这一行
        self.head=(self.head+1)%(self.k+1)
        return True

    def Front(self) -> int:
        if self.isEmpty(): return -1
        return self.data[self.head]

    def Rear(self) -> int:
        if self.isEmpty(): return -1
        return self.data[self.tail-1]

    def isEmpty(self) -> bool:#key
        return self.head==self.tail

    def isFull(self) -> bool: #key
        return (self.tail+1)%(self.k+1)==self.head

# Your MyCircularQueue object will be instantiated and called as such:
# obj = MyCircularQueue(k)
# param_1 = obj.enQueue(value)
# param_2 = obj.deQueue()
# param_3 = obj.Front()
# param_4 = obj.Rear()
# param_5 = obj.isEmpty()
# param_6 = obj.isFull()

lc 380【剑指 030】:O(1) 时间插入、删除和获取随机元素
https://leetcode.cn/problems/insert-delete-getrandom-o1/
提示:
-2^31 <= val <= 2^31 - 1
最多调用 insert、remove 和 getRandom 函数 2 * 105 次
在调用 getRandom 方法时,数据结构中 至少存在一个 元素。
注意:您必须实现类的函数,使每个函数的 平均 时间复杂度为 O(1) ;

class RandomizedSet:

    def __init__(self):
        self.idxmap={}
        self.data=[]

    def insert(self, val: int) -> bool:
        if val in self.idxmap:return False #self.idxmap也对
        #
        self.idxmap[val]=len(self.data)
        self.data.append(val)
        return True

    def remove(self, val: int) -> bool:
        #hashset/map:指定data/key->o(1)
        #数组:根据随机索引->o(1)
        if val not in self.idxmap:return False
        #
        idx=self.idxmap[val]
        lastnum=self.data[-1]
        self.data[idx]=lastnum
        self.data.pop()
        #
        self.idxmap[lastnum]=idx
        del self.idxmap[val]
        return True 

    def getRandom(self) -> int:
        #randint(0,9):0-9
        return self.data[random.randint(0,len(self.data)-1)]#choice(self.data)   
# Your RandomizedSet object will be instantiated and called as such:
# obj = RandomizedSet()
# param_1 = obj.insert(val)
# param_2 = obj.remove(val)
# param_3 = obj.getRandom()

lc 381 :O(1) 时间插入、删除和获取随机元素 - 允许重复
https://leetcode.cn/problems/insert-delete-getrandom-o1-duplicates-allowed/
提示:
-2^31 <= val <= 2^31 - 1
insert, remove 和 getRandom 最多 总共 被调用 2 * 10^5 次
当调用 getRandom 时,数据结构中 至少有一个 元素
注意:您必须实现类的函数,使每个函数的 平均 时间复杂度为 O(1) ;
生成测试用例时,只有在 RandomizedCollection 中 至少有一项 时,才会调用 getRandom
在这里插入图片描述

class RandomizedCollection:
    def __init__(self):
        self.idsmap=defaultdict(set) #for remove o(1) #list也可以
        self.data=[]

    def insert(self, val: int) -> bool:
        self.idsmap[val].add(len(self.data)) #允许重复插入
        self.data.append(val)
        return len(self.idsmap[val])==1 #但,重复插入返回False

    def remove(self, val: int) -> bool:
        if val not in self.idsmap:return False
        #
        idx=self.idsmap[val].pop()
        lastnum=self.data[-1]
        self.data[idx]=lastnum
        self.data.pop() #注意:此时k->k-1
        #key
        #idx已经被pop
        if len(self.idsmap[val])==0:
            del self.idsmap[val]
        
        if idx<len(self.data):#只有val!=lastnum,才有更新必要
            self.idsmap[lastnum].remove(len(self.data))
            self.idsmap[lastnum].add(idx)
        return True

    def getRandom(self) -> int:
        return choice(self.data)

# Your RandomizedCollection object will be instantiated and called as such:
# obj = RandomizedCollection()
# param_1 = obj.insert(val)
# param_2 = obj.remove(val)
# param_3 = obj.getRandom()

缓存机制:三种数据淘汰机制
key:数据Cache【HashMap】+存储顺序【队列/双向链表】
在这里插入图片描述
FIFO 缓存机制
FIFO Cache 代码实现

在这里插入图片描述

# class LocalCache:
#     def __init__(self):
#         self.cache = dict()

#     def get(self, key):
#         return self.cache[key]

#     def put(self, key, value):
#         self.cache[key] = value

class FIFOCache:
    def __init__(self,capacity):
    	self.queue=deque() #key:存键的顺序
    	self.cache={} #dict()
    	self.capacity=capacity
   	
	def get(self,key):
        if key in self.cache:
            return self.cache[key]
        return None
	
	def put(self,key,value):#key-put、change一个逻辑
        #存在->改值
        #不存在->判满->添加
        if key not in self.cache:
            #
            if len(self.cache)==self.capacity:
                del self.cache[self.queue.popleft()]
            #
            self.queue.append(key)
        self.cache[key]=value

lc 146【剑指 031】【top100】:LRU 缓存机制
https://leetcode.cn/problems/lru-cache/
提示:
1 <= capacity <= 3000
0 <= key <= 10000
0 <= value <= 10^5
最多调用 2 * 10^5 次 get 和 put
注意:函数 get 和 put 必须以 O(1) 的平均时间复杂度运行

在这里插入图片描述
在这里插入图片描述

#map+双向链表
#时间:近->久
class Node:
    def __init__(self,key=0,value=0,next=None,prev=None):
        self.key=key
        self.val=value
        self.next=next
        self.prev=prev

class LRUCache:

    def __init__(self, capacity: int):
    	#key:维护时间顺序
        self.dummyhead=Node()
        self.dummytail=Node()
        self.dummyhead.next=self.dummytail
        self.dummytail.prev=self.dummyhead
        #
        self.cache={}
        self.capacity=capacity

    def get(self, key: int) -> int:
        if key not in self.cache:return -1
        #
        node=self.cache[key]
        #key
        self.move_node_to_head(node)
        return node.val

    
    def put(self, key: int, value: int) -> None:
        if key not in self.cache:
            #判满
            if len(self.cache)==self.capacity:
                del_node=self.remove_tailnode()
                del self.cache[del_node.key] #或者self.cache.pop(node.key)
            #
            putnode=Node()
            putnode.key=key
            putnode.val=value
            self.to_head(putnode)
            self.cache[key]=putnode
        else:
        	self.cache[key].val=value
            self.move_node_to_head(self.cache[key])
                    
    def move_node_to_head(self,node):
        self.move_node(node)
        self.to_head(node)

    def move_node(self,node):
        prevnode,nextnode=node.prev,node.next
        #
        node.prev=None
        node.next=None
        prevnode.next=nextnode
        nextnode.prev=prevnode
    
    def to_head(self,node):
        node1=self.dummyhead.next
        self.dummyhead.next=node
        node.prev=self.dummyhead
        node.next=node1
        node1.prev=node

    def remove_tailnode(self):
        del_node=self.dummytail.prev
        self.move_node(del_node)
        return del_node

        
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)

lc 460 :LFU 缓存
https://leetcode.cn/problems/lfu-cache/
提示:
0 <= capacity <= 10^4
0 <= key <= 10^5
0 <= value <= 10^9
最多调用 2 * 10^5 次 get 和 put 方法
注意:函数 get 和 put 必须以 O(1) 的平均时间复杂度运行

在这里插入图片描述

#map+双向链表
#次数同:久->近
#cache元:key->Node(key,val,count)
#计数维护元:count->doublelinkedNode
class Node:
    def __init__(self, key=None,val =None,count =None):
        self.key = key
        self.val = val
        self.count = count
        #
        self.next = None
        self.prev = None

class DoubleLinkedNode:
    def __init__(self):
        self.dummyhead = Node()
        self.dummytail = Node()
        self.dummyhead.next = self.dummytail
        self.dummytail.prev = self.dummyhead

    def remove(self, node) -> Node:
        prevnode,nextnode=node.prev,node.next
        prevnode.next = nextnode
        nextnode.prev = prevnode
        #
        node.prev = None
        node.next = None
        return node

    #拼到表尾
    def append(self, node):
        prevnode = self.dummytail.prev
        prevnode.next=node
        node.prev=prevnode

        node.next = self.dummytail
        self.dummytail.prev = node


    def pop_first(self) -> Node:
        if self.dummyhead.next == self.dummytail:
            return None
        return self.remove(self.dummyhead.next)

    def is_empty(self) -> bool:
        return self.dummyhead.next == self.dummytail

class LFUCache:
    def __init__(self, capacity: int):
        self.used_count_to_nodes = collections.defaultdict(DoubleLinkedNode)
        self.cache = {}
        #
        self.capacity = capacity
        self.min_used_count = 0

    def get(self, key: int) -> int:
        if key not in self.cache:return -1
        node=self.cache[key]
        ######################count的维护和cache的count更新
        count=node.count
        #node.count=count+1 #注意;如果放在这里,node的属性改变,后面remove(node)不通
        self.used_count_to_nodes[count].remove(node)  
        if self.min_used_count==count and self.used_count_to_nodes[count].is_empty():#涉及最小值的更新
            self.min_used_count+=1
        self.used_count_to_nodes[count+1].append(node)
        
        node.count=count+1 
        #
        return node.val

    def put(self, key: int, value: int) -> None:
        if self.capacity == 0: return -1 #注意:此时,del_node(dummytail).key不存在
        
        if key not in self.cache:
            #判满          
            if len(self.cache)==self.capacity:
                del_node=self.used_count_to_nodes[self.min_used_count].pop_first()
                del self.cache[del_node.key] #key
            #       
            putnode=Node(key,value,1)
            self.cache[key]=putnode
            #
            self.used_count_to_nodes[1].append(putnode)
            self.min_used_count=1 
        else:
            node=self.cache[key]
            node.val=value
            #self.cache[key]=node
            self.get(key)#key:还差count的维护和cache的count的更新


# Your LFUCache object will be instantiated and called as such:
# obj = LFUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)

并查集
并查集解决的问题:连接问题
并查集需要支持的两个操作:
使用数组实现并查集(数组的顺序访问的话,JVM会优化,CPU)
树形结构实现并查集 (优化:降低合并的时间复杂度)

使用数组实现并查集
在这里插入图片描述
树形结构实现并查集:
在这里插入图片描述

并查集优化
基于size(总节点数)优化1:缺点-依然可能增加层数,从而增大时间复杂度
基于rank(总层数)优化(+路径压缩)2:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class UnionFind:
    def __init__(self, capacity):
        self.parent=[0]*capacity
        self.rank=[0]*capacity
        #初始化
        for i in range(capacity):
            self.parent[i]=i
            self.rank[i]=1

    def find(self, p):
        if p<0 or p>=len(self.parent):
            raise Exception('p out the range')
        #写法1:迭代
        while (p != self.parent[p]):
            self.parent[p]=slef.parent[slef.parent[p]]#key:路径压缩
            p=self.parent[p]
        return p

        # #写法2:递归
        # if p==self.parent[p]:return self.parent[p]
        # self.parent[p]=self.find[self.parent[p]]#key:路径压缩
        # return parent[p]

    def is_connected(self, p, q):
        return self.find[p]==self.find[q]

    #难点
    def union_element(self, p, q):
        proot,qroot=self.find[p],self.find[q]
        if proot==qroot:return
        if self.rank[proot]<self.rank[qroot]:
            self.parent[proot]=qroot
        elif self.rank[proot]>self.rank[qroot]:
            self.parent[qroot]=proot
        else:
            self.parent[proot]=qroot
            self.rank[qroot]+=1

lc 547【剑指 116】:省份数量
https://leetcode.cn/problems/number-of-provinces/
提示:
1 <= n <= 200
n == isConnected.length
n == isConnected[i].length
isConnected[i][j] 为 1 或 0
isConnected[i][i] == 1
isConnected[i][j] == isConnected[j][i]

在这里插入图片描述

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        rows=len(isConnected)
        cols=len(isConnected[0])
        #
        uf=UnionFind(rows)
        for i in range(rows):
            for j in range(i+1,cols): #右对角
                if isConnected[i][j]==1:
                    uf.union_element(i,j)
        #
        return  uf.getcicles()
                    

class UnionFind:
    def __init__(self, capacity):
        self.parent=[0]*capacity
        self.rank=[0]*capacity

        #初始化
        self.cycles=0
        for i in range(capacity):
            self.parent[i]=i
            self.rank[i]=1
            #key假设省份:最多的状态
            self.cycles+=1 
    
    def find(self, p):
        while p!=self.parent[p]:
            self.parent[p]=self.parent[self.parent[p]]#路径压缩
            p=self.parent[p]
        return self.parent[p]
    
    def is_connected(self, p, q):
        return slef.find(p)==self.find(q)
    
    def union_element(self, p, q):
        proot,qroot=self.find(p),self.find(q)
        if proot==qroot:return 
        if self.rank[proot]<self.rank[qroot]:
            self.parent[proot]=qroot
        elif self.rank[proot]>self.rank[qroot]:
            self.parent[qroot]=proot
        else:
            self.parent[proot]=qroot
            self.rank[qroot]+=1
        #key每合并一次代表少一个省份
        self.cycles-=1

    def getcicles(self):
        return self.cycles

lc 200【top100】:岛屿数量
https://leetcode.cn/problems/number-of-islands/
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] 的值为 ‘0’ 或 ‘1’

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        self.rows=len(grid)
        self.cols=len(grid[0])
        self.dirs=[[-1,0],[1,0],[0,1],[0,-1]]
        #
        uf=UnionFind(grid)
        for i in range(self.rows):
            for j in range(self.cols):
                #key:在‘岛屿’中统计,区别于LC547省份数量
                if grid[i][j]=='1':
                    grid[i][j]='0'
                    for d in self.dirs:
                        nexti,nextj=i+d[0],j+d[1]
                        if self.inarea(nexti,nextj) and grid[nexti][nextj]=='1':
                            uf.union_element(i*self.cols+j,nexti*self.cols+nextj)
        #
        return uf.getcicles()


    def inarea(self,row,col):
        return 0<=row<self.rows and 0<=col<self.cols


class UnionFind:
    def __init__(self, grid):
        rows=len(grid)
        cols=len(grid[0])
        self.parent=[0]*(rows*cols)
        self.rank=[0]*(rows*cols)
        #key初始化
        self.cycles=0
        for i in range(rows):
            for j in range(cols):
                #key:在‘岛屿’中统计,区别于LC547省份数量
                if grid[i][j]=='1':
                    self.parent[i*cols+j]=i*cols+j
                    self.rank[i*cols+j]=1
                    #key假设省份:最多的状态
                    self.cycles+=1 
    
    def find(self, p):
        while p!=self.parent[p]:
            self.parent[p]=self.parent[self.parent[p]]#路径压缩
            p=self.parent[p]
        return self.parent[p]
    
    def is_connected(self, p, q):
        return slef.find(p)==self.find(q)
    
    def union_element(self, p, q):
        proot,qroot=self.find(p),self.find(q)
        if proot==qroot:return 
        if self.rank[proot]<self.rank[qroot]:
            self.parent[proot]=qroot
        elif self.rank[proot]>self.rank[qroot]:
            self.parent[qroot]=proot
        else:
            self.parent[proot]=qroot
            self.rank[qroot]+=1
        #key每合并一次代表少一个省份
        self.cycles-=1

    def getcicles(self):
        return self.cycles

lc 721 :账户合并
https://leetcode.cn/problems/accounts-merge/
提示:
1 <= accounts.length <= 1000
2 <= accounts[i].length <= 10
1 <= accounts[i][j].length <= 30
accounts[i][0] 由英文字母组成
accounts[i][j] (for j > 0) 是有效的邮箱地址
注意:
合并账户后,按以下格式返回账户:每个账户的第一个元素是名称,其余元素是 按字符 ASCII 顺序排列 的邮箱地址。账户本身可以以 任意顺序 返回。
在这里插入图片描述

class Solution:
    def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]:
        #1)建立Map映射-index
        email_to_index,email_to_name={},{}
        index=0
        for account in accounts:
            name=account[0]
            for i in range(1,len(account)):
                if account[i] not in email_to_index:
                    email_to_index[account[i]]=index
                    index+=1
                    email_to_name[account[i]]=name
        #2)建立并查集-index
        uf=UnionFind(index)#非重复总邮件数
        for account in accounts:
            firstindex=email_to_index[account[1]]
            for i in range(2,len(account)):
                nextindex=email_to_index[account[i]]
                uf.union_element(firstindex,nextindex)
        
        #3)合并账户->index
        rootindex_to_emails=collections.defaultdict(list)
        for email,index in email_to_index.items():
            rootindex=uf.find(index)
            rootindex_to_emails[rootindex].append(email)

        #4)合并账户->name
        name_to_emails=list()
        for emails in rootindex_to_emails.values():
            name_to_emails.append([email_to_name[emails[0]]]+sorted(emails))
        
        return name_to_emails


class UnionFind:
    def __init__(self, capacity):
        self.parent = [0] * capacity
        self.rank = [0] * capacity
        for i in range(capacity):
            self.parent[i] = i
            self.rank[i] = 1

    def find(self, p):
        if p < 0 or p >= len(self.parent):
            raise Exception("p 超出了范围")
        while p != self.parent[p]:
            self.parent[p] = self.parent[self.parent[p]]
            p = self.parent[p]
        return p

    def is_connected(self, p, q):
        return self.find(p) == self.find(q)

    def union_element(self, p, q):
        p_root, q_root = self.find(p), self.find(q)
        if p_root == q_root:
            return
        if self.rank[p_root] < self.rank[q_root]:
            self.parent[p_root] = q_root
        elif self.rank[p_root] > self.rank[q_root]:
            self.parent[q_root] = p_root
        else:
            self.parent[q_root] = p_root
            self.rank[p_root] += 1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/77856.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

快看梅西射门了,这是梅西的大力抽射~阿根廷加油,我们是冠军

&#x1f5b1; ⌨个人主页&#xff1a;Love And Program的个人主页 &#x1f496;&#x1f496;如果对你有帮助的话希望三连&#x1f4a8;&#x1f4a8;支持一下博主 来自梅西的大力抽射&#x1f970;致昨晚的梅西思路加入阿根廷元素加入足球元素源码致昨晚的梅西 昨晚上阿根廷…

springcloud 从头开始构建分布式微服务脚手架

必备服务&#xff08;Windows开发本机环境&#xff09; Java maven mysql&#xff1a;自启动服务&#xff0c;后台运行 127.0.0.1:3306 MySQL57 root/root Redis&#xff1a;手动运行&#xff0c;前台运行 127.0.0.1:6379 执行命令redis-server.exe redis.windows.conf na…

Weblogic漏洞 - 通杀方式

文章目录简介恶意文件把恶意文件部署到攻击机&#xff0c;并开启http服务写入文件写入反弹shell命令执行反弹shell命令拿到目标机器权限[linux] WebLogic Server 版本: 12.2.1.3简介 最早在 CVE-2019-2725 被提出&#xff0c;对于所有Weblogic版本均有效。 构造一个XML文件&…

CALC-python和shell对字符的解析差异

好久没看题了&#xff0c;记录一道感觉还挺有意思的题目 一进去题目界面非常简洁&#xff0c;一个计算器 这个简洁的界面&#xff0c;好像似曾相识&#xff0c;总感觉好像以前做题时遇到的ssti题目的界面&#xff0c;果断来一波ssti emem有WAF&#xff0c;尝试下绕过&#xff…

C++ 实现守护进程

文章目录1.守护进程概念1.什么是守护进程2.守护进程的特点3.如何查看linux系统中已存在的守护进程2.守护进程编写的步骤3.示例1.守护进程概念 1.什么是守护进程 Linux Deamon守护进程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或者等待处理某些事…

系统小工具API接口,免费好用

1、前言 系统小工具接口&#xff0c;可生成长短不重复id&#xff0c;可获取服务器标准时间。 查看接口完整信息&#xff1a;https://www.idmayi.com/doc/detail?id23 2、接口明细 注意&#xff1a;app_id和app_secret是临时秘钥&#xff0c;如果真正使用&#xff0c;需要去 ht…

ArrayList源码解析

ArrayList源码解析 简介 ArrayList 是一种以数组实现的 List&#xff0c;与数组相比&#xff0c;它具有动态扩展的能力&#xff0c;因此也可称之为动态数组。 ArrayList 线程不安全&#xff0c;除该类未实现同步外&#xff0c;其余跟 Vector 大致相同。 Java 泛型只是编译器…

赛先生的新旅行:昇腾AI带来的科学智能变革

《三体》动画即将开播&#xff0c;又一次引发了全球科幻迷的无限热情。提到《三体》&#xff0c;大家有很多深刻记忆。其中之一是三体人在入侵地球之前&#xff0c;首先是派智子锁死地球的基础科学进步&#xff0c;从而限制人类的整体发展。很多人会担心&#xff0c;物理、化学…

LeetCode题解12 (146,97) LRU缓存<HashMap + 双向链表>,二叉树的中序遍历

文章目录LRU缓存(146)完整代码解答:二叉树的中序遍历(94)完整代码解答:LRU缓存(146) 从题上看存在Key和Value因此我们采用HashMap来进行存储,这里我们采用HashMap双向链表来实现,我们需要实现里面的get,put方法。 我们需要先创建1个链表节点 //先定义一个节点类 class ListNode…

一个合格的程序员也是一名合格的侦探---Debug篇

调试 文章目录调试1.什么是bug&#xff1f;1.1bug概念1.2bug的起源2.什么是调试&#xff0c;调试的重要性2.1调式的概念2.2 调试的基本步骤2.3Debug版本和Release版本2.3.1Debug版本2.3.2Release版本2.3.3区别2.3.4同一代码在Debug和Release下的差别3.Windows下visual stdio的调…

ioDraw - 免费的在线图表制作工具

介绍&#xff1a; ioDraw是一款数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表&#xff0c;支持折线图、柱状图、饼图、散点图等 地址&#xff1a;https://www.iodraw.com/chart 特点&#xff1a; 图表…

springboot实现License证书的授权和许可到期验证

前言 在客户服务器部署软件项目后&#xff0c;为了项目版权管控或者对项目进行授权收费处理的&#xff0c;就需要实现项目的授权和许可验证。 在这里讲解的是使用 license证书 的形式实现授权和许可验证&#xff08;已通过测试&#xff09;。 主要是通过 IP地址、MAC地址、CP…

PyTorch - 线性回归

文章目录普通实现准备数据反向传播构建模型 实现实例化模型、损失函数、优化器训练数据评估模型普通实现 准备数据 import torch import matplotlib.pyplot as plt # 1、准备数据 # y 2 * x 0.8 x torch.rand([500, 1]) y_true 2 * x 0.8 # 2、通过模型计算 y_predict …

MyBatis面试题(2022最新版)

整理好的MyBatis面试题库&#xff0c;史上最全的MyBatis面试题&#xff0c;MyBatis面试宝典&#xff0c;特此分享给大家 MyBatis简介 MyBatis是什么&#xff1f; MyBatis 是一款优秀的持久层框架&#xff0c;一个半 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它…

Kotlin 开发Android app(二十一):协程launch

什么是协程&#xff0c;这可是这几年才有的概念&#xff0c;我们也不用管它是什么概念&#xff0c;先看看他能做什么。 创建协程 添加依赖&#xff1a; implementation org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9implementation org.jetbrains.kotlinx:kotlinx-cor…

DCDC电感下方铜箔如何处理

挖&#xff1a;电感在工作时&#xff0c;其持续变化的电流产生的电磁波会或多或少的泄露出来&#xff0c;电感下方的铜箔受电磁波影响&#xff0c;就会有涡流出现&#xff0c;这个涡流&#xff0c;①可能对线路板上的信号线有干扰&#xff0c;②铜箔内的涡流会产生热量&#xf…

申请阿里云域名SSL证书步骤

1.【点击登录】 阿里云 2.选择 DV单域名证书 3.确定购买&#xff0c;支付。 4.完成后&#xff0c;跳转回控制台。 5.点击 证书申请。 6.填写域名、申请人姓名、手机号、邮箱、所在地 7、选择域名验证方式&#xff0c;官方提供了三种验证方式&#xff0c;根据自身情况选择其中…

【Linux】Linux的常见指令详解(下)

目录 前言 head/tail 命令行管道 date sort cal 搜索指令 find which whereis alias grep zip tar file bc history 热键 前言 之前讲了Linux的常见指令详解&#xff08;上&#xff09;&#xff0c;这次终于把下也补齐了。如果对你有帮助还麻烦给博主一个…

Netty_05_六种序列化方式(JavaIO序列化 XML序列化 Hessian序列化 JSON序列化 Protobuf序列化 AVRO序列化)(实践类)

文章目录一、普通的序列化方式(bean对象有直接的java类)1.1 普通的java io byteArray输入输出流的序列化方式1.2 xml序列化方式&#xff08;xml用来做配置文件&#xff0c;这样序列化出来长度很大&#xff09;1.3 Hessian序列化方式&#xff08;这个Dubbo中使用的序列化方式&am…

flask前后端项目--实例-前端部分:-3-vue基本配置

一、基本配置以及验证 1.基础环境&#xff1a;nodejs的安装配置以及注意事项 https://blog.csdn.net/wtt234/article/details/128131999 2.vue使用vite创建文件包的过程 创建项目 npm init vitelatest 根据提示一步步选择&#xff1a; 选择vue 进入项目目录&#xff0c;安装…