LeetCode-并查集-Medium

记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步


目录

      • 399. Evaluate Division 除法求值
      • 547. Friend Circles 朋友圈
      • 684. Redundant Connection 冗余连接
      • 721. Accounts Merge 账户合并
      • 947. Most Stones Removed with Same Row or Column 移除最多的同行或同列石头


399. Evaluate Division 除法求值

使用node记录所有成对出现过的数a,b = value
并用weight记录这一对数的值 weight[(a,b)] = value weight[(b,a)] = 1/value
然后对queries内的每一对(x,y)进行dfs:
如果(x,y)已经存在weight中 直接返回值
如果x或者y没有在node中 说明这个未知数没有出现过 直接返回0
如果start已经出现过了 也直接返回0
将这个start加入到visited中
然后遍历与start成对出现过的数node[start]
weight[(start,v)] * dfs(v,end,visited) 及 start到end的值
如果这个值不为零 说明找到了 并将这一对加入到weight 方便以后查询

def calcEquation(equations, values, queries):
    """
    :type equations: List[List[str]]
    :type values: List[float]
    :type queries: List[List[str]]
    :rtype: List[float]
    """
    from collections import defaultdict
    node = defaultdict(set)
    weight = defaultdict()
    ret = []
    
    for i in range(len(equations)):
        x,y = equations[i]
        node[x].add(y)
        node[y].add(x)
        weight[(x,y)] = values[i]
        weight[(y,x)] = 1.0 / values[i]
        
    def dfs(start,end,visited):
        if (start,end) in weight:
            return weight[(start,end)]
        if start not in node or end not in node:
            return 0
        if start in visited:
            return 0
        visited.add(start)
        ans=0
        for v in node[start]:
            ans = weight[(start,v)] * dfs(v,end,visited)
            if ans!=0:
                weight[(start,end)] = ans
                break
        visited.remove(start)
        return ans
    
    for x,y in queries:
        res = dfs(x,y,set())
        if res==0:
            ret.append(-1.0)
        else:
            ret.append(res)
            
    return ret

547. Friend Circles 朋友圈

visit记录已加入朋友圈的人
topv记录他的朋友已经都被加入朋友圈 省去重复判断
依次遍历每一个人 若这个人没有加入朋友圈 则新建一个朋友圈
并通过dfs寻找他的所有朋友加入朋友圈
而这个人的朋友都已加入 所以他会被加入到topv中
dfs:
如果node已加入topv说明这个人所有朋友圈已经被考虑 跳过
否则遍历node的所有未加入visited的朋友 加入朋友圈 并dfs这个朋友

def findCircleNum(M):
    """
    :type M: List[List[int]]
    :rtype: int
    """
    visited=set()
    topv = set()
    ans = 0
    
    def dfs(node):
        if node in topv:
            return
        for i in range(len(M)):
            if i in visited:
                continue
            if M[node][i]==1:
                visited.add(i)
                dfs(i)
                
    for i in range(len(M)):
        if i not in visited:
            print(i)
            ans+=1
            dfs(i)
            topv.add(i)
    return ans

684. Redundant Connection 冗余连接

[a,b]使用end记录节点的连接点 while递归找到终结点 若两个点的终结点不同
说明a,b这条线段连接了两个不同的区域 是必须的
若相同 则说明没有ab也已经连通 ab属于可以去除的线段

def findRedundantConnection(edges):
    """
    :type edges: List[List[int]]
    :rtype: List[int]
    """
    end={}
    res = ""
    for x,y in edges:
        end[x]=x
        end[y]=y
    for tmp in edges:
        x = tmp[0]
        y = tmp[1]
        while x != end[x]:
            x = end[x]
        while y != end[y]:
            y = end[y]
            
        if x==y:
            res = tmp
        else:
            end[x] = y
    return res

721. Accounts Merge 账户合并

根据题目意思 只要两个账号中包含相同的邮箱地址 则认为两个账号可以合并
使用并查集
对每个邮箱创建一个终点邮箱
mailPos: {email:账号位置} 记录所有邮箱对应的账号位置 出现多次对应哪个位置都可以 因为认定为一个账号
rootSet: 存储每个邮箱的终点邮箱 若终点邮箱相同 则代表两个邮箱在同一个账号中
find:
寻找邮箱i的终点邮箱 并返回
union:
寻找邮箱i,j的终点邮箱
若不同
则将i的终点变为j的终点 及将i,j变为同一个账号
遍历已有的账号
将一个账号内的邮箱合并 这里和第一个邮箱合并
结果生成:
遍历所有存在的邮箱
找到其终点邮箱 dic内key为终点邮箱的账号位置 将这个邮箱加入其中
最后加入账号位置上的账号名称

def accountsMerge(accounts):
    """
    :type accounts: List[List[str]]
    :rtype: List[List[str]]
    """
    from collections import defaultdict
    
    
    mailPos = {mail:pos for pos,account in enumerate(accounts) for mail in account[1:]}
    rootSet = {mail:mail for mail in mailPos}
    
    def find(i):
        while rootSet[i]!=i:
            rootSet[i] = rootSet[rootSet[i]]
            i = rootSet[i]
        return i
        
    
    def union(i,j):
        if i!=j:
            endi = find(i)
            endj = find(j)
            if endi!=endj:
                rootSet[endi] = rootSet[j]
    
    for account in accounts:
        l = len(account)
        for i in range(1,l):
            email = account[i]
            union(email,find(account[1]))
                
    dic = defaultdict(set)
    for mail in rootSet:
        pos = mailPos[find(mail)]
        dic[pos].add(mail)
    
    ans = [[accounts[pos][0]]+sorted(mail) for pos,mail in dic.items()]
    return ans

947. Most Stones Removed with Same Row or Column 移除最多的同行或同列石头

难点:读懂题目 第一次看的中文 没看清楚 以为是去除一行或一列…
a move consists of removing a stone that shares a column or row with another stone on the grid.
题目的意思是move操作可以移除一个石头(这个石头的同行或者同列存在其他石头)
所以可以用并查集
将存在于一列或者一样的石子归并到一类 有同样的root石子
同样的find union
最终 每一类n个石子中可以移除n-1个 必定到最后只剩一个没法移除
所以可以移除的最大个数 = 所有个数len(stones) - 所有的类别数len(ret)

def removeStonesError(stones):
    """
    :type stones: List[List[int]]
    :rtype: int
    """
    from collections import defaultdict
    
    row = defaultdict(set)
    col = defaultdict(set)
    rownum = defaultdict(int)
    colnum = defaultdict(int)
    
    for stone in stones:
        pos=(stone[0],stone[1])
        row[stone[0]].add(pos)
        col[stone[1]].add(pos)
        
        rownum[stone[0]]+=1
        colnum[stone[1]]+=1
        
    count=0
        
    while len(stones)>0:
        print("rn:",rownum)
        print("cn:",colnum)
        minrow = min(rownum,key = rownum.get)
        mincol = min(colnum,key = colnum.get)
        count+=1
        if rownum[minrow]<colnum[mincol]:
            tmp = row.pop(minrow)
            rownum.pop(minrow)
            for x,y in tmp:
                pos = (x,y)
                print(pos)
                stones.remove([x,y])
                col[y].remove(pos)
                colnum[y]-=1
                if colnum[y]==0:
                    colnum.pop(y)
        else:
            tmp = col.pop(mincol)
            colnum.pop(mincol)
            for x,y in tmp:
                pos = (x,y)
                print(pos)
                stones.remove([x,y])
                row[x].remove(pos)
                rownum[x]-=1
                if rownum[x]==0:
                    rownum.pop(x)
    print(count)
    return count
        
def removeStones(stones):
    """
    :type stones: List[List[int]]
    :rtype: int
    """  
    from collections import defaultdict
    row = defaultdict(set)
    col = defaultdict(set)
    root = {}
    
    def find(i):
        while root[i]!=i:
            root[i] = root[root[i]]
            i = root[i]
        return i
        
    
    def union(i,j):
        if i!=j:
            endi = find(i)
            endj = find(j)
            if endi!=endj:
                root[endi] = root[j]
                
    for x,y in stones:
        root[(x,y)] = (x,y)
        row[x].add((x,y))
        col[y].add((x,y))
    
    for x,y in stones:
        pos = (x,y)
        for i in row[x]:
            union(i,pos)
        for j in col[y]:
            union(j,pos)
        
    ret = {find((x,y)) for x,y in stones}
    
    ans =len(stones)-len(ret)
    
    return ans

你可能感兴趣的:(Exercise)