图论题目模板,和并查集:以后的图论题目就靠他了

Posted zhangbo2008

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图论题目模板,和并查集:以后的图论题目就靠他了相关的知识,希望对你有一定的参考价值。

技术分享图片
‘‘‘
并查集:
1.用于查如何A,B是否在一个集合中.
2.每一个集合设立一个头结点.其他都连向他
3.集合合并就是把小的集合挂到大的集合下面即可
4.优化.查询到一个a在b这个头结点下面,那么直接把a.next=b
‘‘‘
class bingcha():
    def __init__(self):

        self.fathermap={}
        self.sizemap={}
    def make_sets(self,list1):#把数据集list赋值到并查集里面做初始化
        for i in range(len(list1)):
            self.fathermap[list1[i]]=list1[i]
            self.sizemap[list1[i]]=1
    def find_father(self,node):#返回node的父节点是谁,然后把node挂到父节点上.
        father=node
        if self.fathermap[node]!=node:
            father=self.find_father(self.fathermap[node])
            self.fathermap[node]=father

        return father
    def union(self,node1,node2):
        father1=self.find_father(node1)
        father2=self.find_father(node2)
        if father1!=father2:
            size1=self.sizemap[father1]
            size2=self.sizemap[father2]
            if size1>=size2:
                self.fathermap[node2]=father1
                self.sizemap[father1]+=size2
            else:
                self.fathermap[node1]=father2
                self.sizemap[father2]+=size1
    def in_same_set(self,node1,node2):
        return self.find_father(node1)==self.find_father(node2)
a=bingcha()
a.make_sets([1,2,3,4,5])
a.union(1,2)
a.union(1,3)
a.union(1,4)
print(a.in_same_set(2,4))
print(a.find_father(4))      #解决了并查集的代码实现.从直观上也能看出来,当已经查询或者插入了N次
                            #再进行查询操作的画效率O(1).因为都已经连到根了.搜索1次即可.
                            #继续理解并查集:他用树的加速来实现了并和查的操作,虽然他效率非常快,
                            #但是不能进行交的操作.这就是他跟set的区别.set复杂度O(N).
             #并查集在图里面很实用.虽然面试对图考的不多,但是应该掌握.
















#下面就是左神给的图论问题模板,非常强大.能实现所有图论问题
#使用方法:对有向图就用graphgenerate,插入所有边即可.顺道就所有点都有了
#         对无向图,就插入2次,一次是from to 一次是to from即可.
‘‘‘
开始搞图:设计好几个类,然后存图,左神给的是邻接数组.
‘‘‘
class node():
    def __init__(self,val):
        self.val=val
        self.in1=0
        self.out=0
        self.nexts=[]
        self.edges=[]
class edge():
    def __init__(self,weight,from1,to):
        self.weight=weight
        self.from1=from1
        self.to=to
    #需要手动写上这几个比较函数.
    def __cmp__(self,other):
      return cmp(self.weight, other.weight)
    def __lt__(self,other):#operator < 
        return self.weight < other.weight
    def __ge__(self,other):#oprator >=
        return self.weight >= other.weight
    def __gt__(self,other):#oprator >=
        return self.weight > other.weight
    def __le__(self,other):#oprator <=
        return self.weight <= other.weight

class Graph():
    def __init__(self):
        self.nodes={}       #结构是key是题目给的编号,value是自己构造的node节点对象.
                              #node.value也是编号.
                           #因为要做复杂处理,所以第一步都是把对应编号转化成为node对象来进行处理.
        self.edges=set()
def GraphGenerator(matrix):#给矩阵,每一行都是 from,end,边长, 3个元素组成.
    graph=Graph()
    for i in range(len(matrix)):
        from1=matrix[i][0]
        to=matrix[i][1]
        weight=matrix[i][2]
        graph.nodes.setdefault(from1,node(from1))
        graph.nodes.setdefault(to,node(to))
        fromNode=graph.nodes[from1]
        toNode=graph.nodes[to]
        newEdge=edge(weight,fromNode,toNode)#这里面用node来做edge参数好么?
        fromNode.nexts.append(toNode)
        fromNode.out+=1
        toNode.in1+=1
        fromNode.edges.append(newEdge)
        graph.edges.add(newEdge)
    return graph



‘‘‘
宽度优先遍历也叫广度优先遍历.
‘‘‘
‘‘‘
先写宽度便利:#利用一个队列和一个set
‘‘‘
import queue
def bfs(node):
    q=queue.Queue()
    q.put(node)
    visited=set([node])
    while q.empty()==False:
        tmp=q.get()
        
        print(tmp.val)#遍历的操作
        for i in tmp.nexts:
            if i not in visited:
             visited.add(i)
             q.put(i)
graph=GraphGenerator([[1,2,3],[2,4,5],[2,6,7],[4,6,5],[1,6,99],[99,98,999]])
print(ceshi)

(bfs(graph.nodes[1])) #graph.nodes[1]表示1号节点对应的node

‘‘‘
深度优先:只用一个set就行
‘‘‘

##我自己写的菜鸟版本.函数每次都带visited.太慢了.左神用的是栈,来直接模拟递归过程.
#def dfs(node): #为了设立局部所以用函数嵌套来写,因为第一次运行时候,跟后面的代码要不同,
#              #多一行初始化visited,然后后面为了保持每个visited在每一次函数时候都一样,就传进去.
#    visited=set([node])
#    def mini_dfs(node,visited):
        
#        print(node.val)#遍历的操作
#        for i in node.nexts:
#            if i not in visited:
#               mini_dfs(i,visited)
#               visited.add(i)
#    mini_dfs(node,visited)
#    return 
#dfs(graph.nodes[1])


def dfs(node):#大神的版本
     visited=set()
     fuzhu=[node]
     
     while fuzhu!=[]:
         node=fuzhu.pop()
         if node not in visited:
             print(node.val)
         visited.add(node) #node打印过了,就赶紧把他放visited里面.避免重复访问.
         for i in node.nexts:
            if i not in visited:#如果还有node的儿子i是没有访问过的,那么需要把node,i压回去.
                                #就是用这个栈来替代递归过程.也就是递归改递推.
               fuzhu.append(node)
               fuzhu.append(i)
               break
print(ceshi2)
dfs(graph.nodes[1])        
‘‘‘
拓扑排序:
找到全部入度为0的,全做完,然后删除这些节点相关的点和边,继续循环即可.
‘‘‘
def tuopu(graph):#任何一个有向无环图才可以拓扑排序
    a=queue.Queue()
    for i in graph.nodes.values():
        if i.in1==0:#入度是0.in是关键字没发使用,所以改成in1
            a.put(i)
    result=[]
    while a.empty()==False:
        tmp=a.get()
        result.append(tmp)
        for i in tmp.nexts:
            i.in1-=1
            if i.in1==0:
                a.put(i)
    return result
print(测试3)
for i in tuopu(graph):
    print(i.val)


‘‘‘
最小生成树p算法.
‘‘‘

‘‘‘
最小生成树k算法. 这尼玛:直接贪心,每一次都选权重最小的边.不产生回路就加进来.
最后所有点都有了,就结束.你妈这么简单??????居然不会产生bug.顿时感觉最小生成树很low

左神的最牛逼代码,用并查集来判断回路.有公共祖先就是有回路.
‘‘‘
from queue import PriorityQueue as PQueue

import heapq
def krustalMST(graph):#k算法实在是太短了.同样下面K算法也可以处理不连通的情况
    output=set()
    a=bingcha()
    a1=list(graph.nodes.values())
    a.make_sets(a1)#把所有nodes放并查集里面
    pq = PQueue()#给edge类加一个cmp方法即可.
    for i in graph.edges:
        pq.put(i)
    while pq.empty()!=True:
        
        tmp=pq.get()
        #如果tmp的from 和to 是在并查集里面有公共祖先的就不要了
        if a.in_same_set(tmp.from1,tmp.to)!=True:#表示不是环路
            #那么就加入这个变
            output.add(tmp)
            
            a.union(tmp.from1,tmp.to)
    
    return output#返回的是边的set

print(ceshi4)

for i in krustalMST(graph):
    print(i.weight,i.from1.val,i.to.val)#效果可以.虽然是针对无向图,但是没有插入反向from1,to也效果
                                       #一样,因为我们考察的是边.


‘‘‘
最小生成树:P算法.随便加进去一个点,然后找这个点的edge,加到pq里面,pq弹出一个最小的,加入tonode.循环
即可.
‘‘‘
def PrimMST(graph):
    output=set()
    result=set()
    pq=PQueue()
    for i in graph.nodes.values():#这个循环来处理整个图不联通的情况.
        if i not in output:
            output.add(i)
            for edge in i.edges:
                pq.put(edge)
            while pq.empty()!=True:
               tmp=pq.get()#道理都是每一次尽量走最短的边,
               if tmp.to not in output:
                   result.add(tmp)#当弹出的边正好to节点没有访问,就是我们要的,放入result中!
                   output.add(tmp.to)
                   for pp in tmp.to.edges:
                      pq.put(pp) #当新插入节点后,边的队列pq也更新一下即可.
    return result
print(ceshi5)
for i in PrimMST(graph):
    print(i.weight,i.from1.val,i.to.val)
            
View Code

 

以上是关于图论题目模板,和并查集:以后的图论题目就靠他了的主要内容,如果未能解决你的问题,请参考以下文章

stl 和并查集应用

并查集解决图论问题(连通性及有无圈存在)

图论:Tarjan算法

专题四-图论总结

专题四-图论总结

算法总结图论-并查集