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