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

'''
并查集:
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

 完美的加入了低价斯特拉算法. 单元最短路径算法.用辅助set工具避免了重新写堆算法.

代码量很少.效率应该是VlogE

'''
https://www.nowcoder.com/live/11?page=1
还是继续刷左程云的算法题.

2.给定一个非负数的数组, 代表一个容器。 例如数组[0,1,0,2,1,0,1,3,2,1,2,1], 就是
以下图形中黑色的部分。如果用这个容器接水的话, 请问可以接多少水? 还以这个数组为例,
可以接 6 格水, 就是以下图形中蓝色的部分。

3.给定一个非负数的数组, 数组中的每个值代表一个柱子的高度, 柱子的宽度是 1。 两个柱
子之间可以围成一个面积, 规定: 面积=两根柱子的最小值* 两根柱子之间的距离。 比如数
组[3,4,2,5]。 34 之间围成的面积为 0, 因为两个柱子是相邻的, 中间没有距离。 32 之间围成的面积为 2, 因为两个柱子的距离为 1, 且 2 是最短的柱子, 所以面积=1*235 之间围成的面积为 6, 因为两个柱子的距离为 2, 且 3 是最短的柱子, 所以面积=
3*2。 求在一个数组中, 哪两个柱子围成的面积最大, 并返回值。
#第三个题目显然就是单调栈的使用.
第二个题目一直没看懂.看懂了,就是第一节课课件里面的第三页的图片.
本质就是求数组的波谷即可.
第二个题目就是本质是求滑动窗口的最大值问题.比如位置i
那么就求比i小的位置的最大值,和比i大的位置的最大值.两个最大值最小的那个如果比i位置的数
大,那么我就返回这个差值就是i位置能存水的数量.
我去:滑动窗口的最大值问题.本质双端队列.
'''

'''
1.求两个子数组最大的累加和
【题目】
给定一个数组, 其中当然有很多的子数组, 在所有两个子数组的组合中, 找到相
加和最大的一组, 要求两个子数组无重合的部分。 最后返回累加和。
【要求】
时间复杂度达到 O(N)

想到按每一个分店来分,然后分别算2个.那么算法是N^2的.
但是要N怎么算?
辅助数组么:
怎么个辅助数组法.
我开一个left数组:left[i]表示原来list[:i]上面的子数组最大累加和.
right[i]............................list[i:]...................
然后我i位置2个部分的最大累加和就是left[i]+right[i].
这样就O(N)了.
'''
def main(list1):
    def qiu_left(list1):
        out = -float('inf')
        sum = 0
        p = []
        for i in range(len(list1)):
            sum+=list1[i]
            if sum > out:
                out = sum
            
            p.append(out)
            if sum < 0:
                sum = 0
        return p
    def qiu_right(list1):
        return qiu_left(list1[::-1])
    a = qiu_left(list1)
    b = qiu_right(list1)
    out = -float('inf')
    for i in range(len(list1) - 1):#注意这里的范围.
        tmp = a[i] + b[len(list1) - i - 2]
        if tmp > out:
            out = tmp
    return out
print(main([32,432,65,675,-999,-65756]))


'''
2.未排序正数数组中累加和为给定值的最长子数组长度
【题目】
给定一个数组 arr, 该数组无序, 但每个值均为正数, 再给定一个正数 k。 求 arr
的所有子数组中所有元素相加和为 k 的最长子数组长度。
例如, arr=[1,2,1,1,1], k=3。
累加和为 3 的最长子数组为[1,1,1], 所以结果返回 3。
【要求】
时间复杂度 O(N), 额外空间复杂度 O(1)
●类似背包问题,求出所有能拼成k的子数组的长度然后取max,但是复杂度很高.
貌似也不太高,用一个指针来表示读到那个位置即可.但是达不到O(N).
●感觉还是辅助数组.首先的到数组sum:sum[i]表示原来数组从0到i的求和.
把sum里面元素放哈希表里面key是sum[i] val 是i
然后给sum[i]找是否存在k+sum[i].整体效率N
●左神破题点:子数组问题:必须以那个位置作为结尾的情况下怎么怎么样...这是面试里面的套路.
'''
#经过左神的讲解.原来是双指针都在左面,然后开始移动,然后维护指针括住区域的sum即可.
def main(list1,k):
    left = 0
    right = 0
    sum = list1[0]
    p = -1
    while right <= len(list1) - 1:
        if sum == k:
            tmp = right - left + 1
            if tmp > p:
                p = tmp
        if sum > k:
            sum-=list1[left]
            left+=1
        if sum <= k and right < len(list1) - 1: #便捷条件要扣
            sum+=list1[right + 1]
            right+=1
        if right == len(list1) - 1 and sum <= k: #便捷条件要扣
            break
    return p
print(main([3,5,6,8,1,1,1,1,1,0.5,0.5,0.5,0.3],4))
'''
https://www.nowcoder.com/live/2/17/1
'''
'''
网盘内容:https://www.52pojie.cn/forum.php?mod=viewthread&tid=726182
'''
'''
学习第六课.图论算法.我学的最差的东西,也是结构性强,套路性强的题目,比赛很喜欢靠图论题目.
写过算法导论里面的,但是理解很差.也记不住.
'''
'''
美团2016笔试题目:
一个数组.求array[b]-array[a]最大值,并且b比a大.
N^2随便写,但是要N的效率呢?题目显然要一个切分点,那么每个切分之后效率是O(1).
这种效率一般都是预处理数组才能做到.所以需要做一个数组1[i]表示从0到i最小值.
一个数组2[i]表示从i到最后的最大值.然后两个一剪即可.
'''
def main(list1):
    mini = []
    zuixiao = float('inf')
    for i in range(len(list1)):
        if list1[i] < zuixiao:
            zuixiao = list1[i]
        mini.append(zuixiao)
    list1 = list1[::-1]
    maxi = []
    zuida = -float('inf')
    for i in range(len(list1)):
        if list1[i] > zuixiao:
            zuida = list1[i]
        maxi.append(zuida)

    out = -float('inf')
    for i in range(len(list1)):
        tmp = maxi[i] - mini[len(list1) - 2 - i]
        if tmp > out:
            out = tmp
    return out
print(main([2,4,6,8,9,5,99999,-265]))#测试一下基本对了.强大的预处理数组技巧.美团的题目还是水平可以的.
'''
并查集:
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 = []
        self.distance=float('inf')
    def __cmp__(self,other):
        return cmp(self.distance, other.distance)
    def __lt__(self,other):#operator <
        return self.distance < other.distance
    def __ge__(self,other):#oprator >=
        return self.distance >= other.distance
    def __gt__(self,other):#oprator >=
        return self.distance > other.distance
    def __le__(self,other):#oprator <=
        return self.distance <= other.distance
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就行
'''

#左神用的是栈,来直接模拟递归过程.好像跟递归写法差不多
def dfs_me(node): 
    visited = set([node])#set或者list自动是全局变量.在多少重子函数里面他都是能调用外面的list.
    def mini_dfs(node):
        
        print(node.val)#遍历的操作
        for i in node.nexts:
            if i not in visited:
               mini_dfs(i)
               visited.add(i)
    mini_dfs(node)
    return 
print('ceshi0')
dfs_me(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

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)
            
'''
慕课网课程:
自环边,平行边.有这2种边的叫复杂图.
我们上面左神的表达方法:就是邻接表.
'''
'''
单源最短路径:直接动态规划即可.很容易
显然满足最优子结构.a到b再到c的最短路径.一定是a到b的最短路径加上b到c的最短路径.
'''
'''
dijkstra 地理课死啦算法.前提:图中不能有负权边.
'''
#格式刷快捷键ctrl+k  再 ctrl+f
def dijkstra(graph,start):#为了记录路径直接在node里面加一个last_node属性即可.他记录了这个节点在最短路径
                    #这个过程里面他的上一步是哪个节点.
                                              #然后继续加一个distance属性,来记录这个节点他距离最开始节点的距离.
        
          #start是一个node对象.                                   
        visited = set()
        pq = PQueue()
        pq_assist=set() #用来判断元素是否在pq中.pq加入元素他就加入元素,pq删除他就删除!
                       #总之我这么构造的原因就是为了避免重新修改heapq代码.和node类
        #初始化start点
        start.distance=0
        start.last_node=start
        visited.add(start)

        #直接在优先队列里面存入node.只存入node即可.排序函数里面写distance即可.
        #修改时候直接修改node即可,对队列里面数值进行修改.
        pq.put(start)
        pq_assist.add(start)
        while pq.empty()!=True:
            tmp=pq.get() #所以一开始这个tmp就是start
            pq_assist.remove(tmp)
            visited.add(tmp)
            #找tmp相邻的边.
            for i in tmp.edges:
                obj=i.to#obj是新节点v相连的to节点.
                if obj not in visited:
                    if tmp.distance+i.weight<obj.distance:
                        obj.distance=tmp.distance+i.weight#说是松弛操作,但是看起来非常像拉紧操作!他起名时脑袋进水了
                        obj.last_node=tmp
                    if obj not in pq_assist:
                        pq.put(obj)
                        pq_assist.add(obj)
a=PQueue()
a.put(3)
a.put(3)
a.put(3)
a.put(3)

#graph = GraphGenerator([[1,2,3],[2,4,5],[2,6,7],[4,6,5],[1,6,99],[99,98,999]])这个是测试用的图
dijkstra(graph,graph.nodes[1])
print(graph.nodes[98].distance)
View Code

 继续修改

'''
https://www.nowcoder.com/live/11?page=1
还是继续刷左程云的算法题.

2.给定一个非负数的数组, 代表一个容器。 例如数组[0,1,0,2,1,0,1,3,2,1,2,1], 就是
以下图形中黑色的部分。如果用这个容器接水的话, 请问可以接多少水? 还以这个数组为例,
可以接 6 格水, 就是以下图形中蓝色的部分。

3.给定一个非负数的数组, 数组中的每个值代表一个柱子的高度, 柱子的宽度是 1。 两个柱
子之间可以围成一个面积, 规定: 面积=两根柱子的最小值* 两根柱子之间的距离。 比如数
组[3,4,2,5]。 34 之间围成的面积为 0, 因为两个柱子是相邻的, 中间没有距离。 32 之间围成的面积为 2, 因为两个柱子的距离为 1, 且 2 是最短的柱子, 所以面积=1*235 之间围成的面积为 6, 因为两个柱子的距离为 2, 且 3 是最短的柱子, 所以面积=
3*2。 求在一个数组中, 哪两个柱子围成的面积最大, 并返回值。
#第三个题目显然就是单调栈的使用.
第二个题目一直没看懂.看懂了,就是第一节课课件里面的第三页的图片.
本质就是求数组的波谷即可.
第二个题目就是本质是求滑动窗口的最大值问题.比如位置i
那么就求比i小的位置的最大值,和比i大的位置的最大值.两个最大值最小的那个如果比i位置的数
大,那么我就返回这个差值就是i位置能存水的数量.
我去:滑动窗口的最大值问题.本质双端队列.
'''

'''
1.求两个子数组最大的累加和
【题目】
给定一个数组, 其中当然有很多的子数组, 在所有两个子数组的组合中, 找到相
加和最大的一组, 要求两个子数组无重合的部分。 最后返回累加和。
【要求】
时间复杂度达到 O(N)

想到按每一个分店来分,然后分别算2个.那么算法是N^2的.
但是要N怎么算?
辅助数组么:
怎么个辅助数组法.
我开一个left数组:left[i]表示原来list[:i]上面的子数组最大累加和.
right[i]............................list[i:]...................
然后我i位置2个部分的最大累加和就是left[i]+right[i].
这样就O(N)了.
'''
def main(list1):
    def qiu_left(list1):
        out = -float('inf')
        sum = 0
        p = []
        for i in range(len(list1)):
            sum+=list1[i]
            if sum > out:
                out = sum
            
            p.append(out)
            if sum < 0:
                sum = 0
        return p
    def qiu_right(list1):
        return qiu_left(list1[::-1])
    a = qiu_left(list1)
    b = qiu_right(list1)
    out = -float('inf')
    for i in range(len(list1) - 1):#注意这里的范围.
        tmp = a[i] + b[len(list1) - i - 2]
        if tmp > out:
            out = tmp
    return out
print(main([32,432,65,675,-999,-65756]))


'''
2.未排序正数数组中累加和为给定值的最长子数组长度
【题目】
给定一个数组 arr, 该数组无序, 但每个值均为正数, 再给定一个正数 k。 求 arr
的所有子数组中所有元素相加和为 k 的最长子数组长度。
例如, arr=[1,2,1,1,1], k=3。
累加和为 3 的最长子数组为[1,1,1], 所以结果返回 3。
【要求】
时间复杂度 O(N), 额外空间复杂度 O(1)
●类似背包问题,求出所有能拼成k的子数组的长度然后取max,但是复杂度很高.
貌似也不太高,用一个指针来表示读到那个位置即可.但是达不到O(N).
●感觉还是辅助数组.首先的到数组sum:sum[i]表示原来数组从0到i的求和.
把sum里面元素放哈希表里面key是sum[i] val 是i
然后给sum[i]找是否存在k+sum[i].整体效率N
●左神破题点:子数组问题:必须以那个位置作为结尾的情况下怎么怎么样...这是面试里面的套路.
'''
#经过左神的讲解.原来是双指针都在左面,然后开始移动,然后维护指针括住区域的sum即可.
def main(list1,k):
    left = 0
    right = 0
    sum = list1[0]
    p = -1
    while right <= len(list1) - 1:
        if sum == k:
            tmp = right - left + 1
            if tmp > p:
                p = tmp
        if sum > k:
            sum-=list1[left]
            left+=1
        if sum <= k and right < len(list1) - 1: #便捷条件要扣
            sum+=list1[right + 1]
            right+=1
        if right == len(list1) - 1 and sum <= k: #便捷条件要扣
            break
    return p
print(main([3,5,6,8,1,1,1,1,1,0.5,0.5,0.5,0.3],4))
'''
https://www.nowcoder.com/live/2/17/1
'''
'''
网盘内容:https://www.52pojie.cn/forum.php?mod=viewthread&tid=726182
'''
'''
学习第六课.图论算法.我学的最差的东西,也是结构性强,套路性强的题目,比赛很喜欢靠图论题目.
写过算法导论里面的,但是理解很差.也记不住.
'''
'''
美团2016笔试题目:
一个数组.求array[b]-array[a]最大值,并且b比a大.
N^2随便写,但是要N的效率呢?题目显然要一个切分点,那么每个切分之后效率是O(1).
这种效率一般都是预处理数组才能做到.所以需要做一个数组1[i]表示从0到i最小值.
一个数组2[i]表示从i到最后的最大值.然后两个一剪即可.
'''
def main(list1):
    mini = []
    zuixiao = float('inf')
    for i in range(len(list1)):
        if list1[i] < zuixiao:
            zuixiao = list1[i]
        mini.append(zuixiao)
    list1 = list1[::-1]
    maxi = []
    zuida = -float('inf')
    for i in range(len(list1)):
        if list1[i] > zuixiao:
            zuida = list1[i]
        maxi.append(zuida)

    out = -float('inf')
    for i in range(len(list1)):
        tmp = maxi[i] - mini[len(list1) - 2 - i]
        if tmp > out:
            out = tmp
    return out
print(main([2,4,6,8,9,5,99999,-265]))#测试一下基本对了.强大的预处理数组技巧.美团的题目还是水平可以的.
'''
并查集:
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 = []
        self.distance=float('inf')
    def __cmp__(self,other):
        return cmp(self.distance, other.distance)
    def __lt__(self,other):#operator <
        return self.distance < other.distance
    def __ge__(self,other):#oprator >=
        return self.distance >= other.distance
    def __gt__(self,other):#oprator >=
        return self.distance > other.distance
    def __le__(self,other):#oprator <=
        return self.distance <= other.distance
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就行
'''

#左神用的是栈,来直接模拟递归过程.好像跟递归写法差不多
def dfs_me(node): 
    visited = set([node])#set或者list自动是全局变量.在多少重子函数里面他都是能调用外面的list.
    def mini_dfs(node):
        
        print(node.val)#遍历的操作
        for i in node.nexts:
            if i not in visited:
               mini_dfs(i)
               visited.add(i)
    mini_dfs(node)
    return 
print('ceshi0')
dfs_me(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

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)
            
'''
慕课网课程:
自环边,平行边.有这2种边的叫复杂图.
我们上面左神的表达方法:就是邻接表.
'''
'''
单源最短路径:直接动态规划即可.很容易
显然满足最优子结构.a到b再到c的最短路径.一定是a到b的最短路径加上b到c的最短路径.
'''
'''
dijkstra 地理课死啦算法.前提:图中不能有负权边.
'''
#格式刷快捷键ctrl+k  再 ctrl+f
def dijkstra(graph,start):#为了记录路径直接在node里面加一个last_node属性即可.他记录了这个节点在最短路径
                    #这个过程里面他的上一步是哪个节点.
                                              #然后继续加一个distance属性,来记录这个节点他距离最开始节点的距离.
        
          #start是一个node对象.                                   
        visited = set()
        pq = PQueue()
        pq_assist=set() #用来判断元素是否在pq中.pq加入元素他就加入元素,pq删除他就删除!
                       #总之我这么构造的原因就是为了避免重新修改heapq代码.和node类
        #初始化start点
        start.distance=0
        start.last_node=start
        visited.add(start)

        #直接在优先队列里面存入node.只存入node即可.排序函数里面写distance即可.
        #修改时候直接修改node即可,对队列里面数值进行修改.
        pq.put(start)
        pq_assist.add(start)
        while pq.empty()!=True:
            tmp=pq.get() #所以一开始这个tmp就是start
            pq_assist.remove(tmp)
            visited.add(tmp)
            #找tmp相邻的边.
            for i in tmp.edges:
                obj=i.to#obj是新节点v相连的to节点.
                if obj not in visited:
                    if tmp.distance+i.weight<obj.distance:
                        obj.distance=tmp.distance+i.weight#说是松弛操作,但是看起来非常像拉紧操作!他起名时脑袋进水了
                        obj.last_node=tmp
                    if obj not in pq_assist:
                        pq.put(obj)
                        pq_assist.add(obj)
a=PQueue()
a.put(3)
a.put(3)
a.put(3)
a.put(3)

#graph = GraphGenerator([[1,2,3],[2,4,5],[2,6,7],[4,6,5],[1,6,99],[99,98,999]])这个是测试用的图
dijkstra(graph,graph.nodes[1])
print(graph.nodes[6].last_node.val)
'''
总结一下我写的这个函数:
1.利用了辅助set技巧.来实现对队列里面元素的跟踪看他是否在队列中.
话说一个队列连一个in方法都没有,很坑!
2.设计最重要的部分,是因为python对于一个对象,如果把它放到任意容器中,你只要修改对象的属性,那么
在容器中的这个对象也自动修改.(同一个对象的本质应该是引用.)利用这点直接在队列外面修改对象属性
即可.不用管队列内部了.其实也没法访问队列内部,队列不能随机存取.用对象就避免了这个复杂操作.
从而提高时间效率.
3.对于最短路径的跟踪方法.也是用对象里面加入一个last_node属性.取值是一个node对象.
利用他就能查询最短路径的上一个节点是哪个节点了.
'''
View Code

 继续添加功能

'''
https://www.nowcoder.com/live/11?page=1
还是继续刷左程云的算法题.

2.给定一个非负数的数组, 代表一个容器。 例如数组[0,1,0,2,1,0,1,3,2,1,2,1], 就是
以下图形中黑色的部分。如果用这个容器接水的话, 请问可以接多少水? 还以这个数组为例,
可以接 6 格水, 就是以下图形中蓝色的部分。

3.给定一个非负数的数组, 数组中的每个值代表一个柱子的高度, 柱子的宽度是 1。 两个柱
子之间可以围成一个面积, 规定: 面积=两根柱子的最小值* 两根柱子之间的距离。 比如数
组[3,4,2,5]。 34 之间围成的面积为 0, 因为两个柱子是相邻的, 中间没有距离。 32 之间围成的面积为 2, 因为两个柱子的距离为 1, 且 2 是最短的柱子, 所以面积=1*235 之间围成的面积为 6, 因为两个柱子的距离为 2, 且 3 是最短的柱子, 所以面积=
3*2。 求在一个数组中, 哪两个柱子围成的面积最大, 并返回值。
#第三个题目显然就是单调栈的使用.
第二个题目一直没看懂.看懂了,就是第一节课课件里面的第三页的图片.
本质就是求数组的波谷即可.
第二个题目就是本质是求滑动窗口的最大值问题.比如位置i
那么就求比i小的位置的最大值,和比i大的位置的最大值.两个最大值最小的那个如果比i位置的数
大,那么我就返回这个差值就是i位置能存水的数量.
我去:滑动窗口的最大值问题.本质双端队列.
'''

'''
1.求两个子数组最大的累加和
【题目】
给定一个数组, 其中当然有很多的子数组, 在所有两个子数组的组合中, 找到相
加和最大的一组, 要求两个子数组无重合的部分。 最后返回累加和。
【要求】
时间复杂度达到 O(N)

想到按每一个分店来分,然后分别算2个.那么算法是N^2的.
但是要N怎么算?
辅助数组么:
怎么个辅助数组法.
我开一个left数组:left[i]表示原来list[:i]上面的子数组最大累加和.
right[i]............................list[i:]...................
然后我i位置2个部分的最大累加和就是left[i]+right[i].
这样就O(N)了.
'''
def main(list1):
    def qiu_left(list1):
        out = -float('inf')
        sum = 0
        p = []
        for i in range(len(list1)):
            sum+=list1[i]
            if sum > out:
                out = sum
            
            p.append(out)
            if sum < 0:
                sum = 0
        return p
    def qiu_right(list1):
        return qiu_left(list1[::-1])
    a = qiu_left(list1)
    b = qiu_right(list1)
    out = -float('inf')
    for i in range(len(list1) - 1):#注意这里的范围.
        tmp = a[i] + b[len(list1) - i - 2]
        if tmp > out:
            out = tmp
    return out
print(main([32,432,65,675,-999,-65756]))


'''
2.未排序正数数组中累加和为给定值的最长子数组长度
【题目】
给定一个数组 arr, 该数组无序, 但每个值均为正数, 再给定一个正数 k。 求 arr
的所有子数组中所有元素相加和为 k 的最长子数组长度。
例如, arr=[1,2,1,1,1], k=3。
累加和为 3 的最长子数组为[1,1,1], 所以结果返回 3。
【要求】
时间复杂度 O(N), 额外空间复杂度 O(1)
●类似背包问题,求出所有能拼成k的子数组的长度然后取max,但是复杂度很高.
貌似也不太高,用一个指针来表示读到那个位置即可.但是达不到O(N).
●感觉还是辅助数组.首先的到数组sum:sum[i]表示原来数组从0到i的求和.
把sum里面元素放哈希表里面key是sum[i] val 是i
然后给sum[i]找是否存在k+sum[i].整体效率N
●左神破题点:子数组问题:必须以那个位置作为结尾的情况下怎么怎么样...这是面试里面的套路.
'''
#经过左神的讲解.原来是双指针都在左面,然后开始移动,然后维护指针括住区域的sum即可.
def main(list1,k):
    left = 0
    right = 0
    sum = list1[0]
    p = -1
    while right <= len(list1) - 1:
        if sum == k:
            tmp = right - left + 1
            if tmp > p:
                p = tmp
        if sum > k:
            sum-=list1[left]
            left+=1
        if sum <= k and right < len(list1) - 1: #便捷条件要扣
            sum+=list1[right + 1]
            right+=1
        if right == len(list1) - 1 and sum <= k: #便捷条件要扣
            break
    return p
print(main([3,5,6,8,1,1,1,1,1,0.5,0.5,0.5,0.3],4))
'''
https://www.nowcoder.com/live/2/17/1
'''
'''
网盘内容:https://www.52pojie.cn/forum.php?mod=viewthread&tid=726182
'''
'''
学习第六课.图论算法.我学的最差的东西,也是结构性强,套路性强的题目,比赛很喜欢靠图论题目.
写过算法导论里面的,但是理解很差.也记不住.
'''
'''
美团2016笔试题目:
一个数组.求array[b]-array[a]最大值,并且b比a大.
N^2随便写,但是要N的效率呢?题目显然要一个切分点,那么每个切分之后效率是O(1).
这种效率一般都是预处理数组才能做到.所以需要做一个数组1[i]表示从0到i最小值.
一个数组2[i]表示从i到最后的最大值.然后两个一剪即可.
'''
def main(list1):
    mini = []
    zuixiao = float('inf')
    for i in range(len(list1)):
        if list1[i] < zuixiao:
            zuixiao = list1[i]
        mini.append(zuixiao)
    list1 = list1[::-1]
    maxi = []
    zuida = -float('inf')
    for i in range(len(list1)):
        if list1[i] > zuixiao:
            zuida = list1[i]
        maxi.append(zuida)

    out = -float('inf')
    for i in range(len(list1)):
        tmp = maxi[i] - mini[len(list1) - 2 - i]
        if tmp > out:
            out = tmp
    return out
print(main([2,4,6,8,9,5,99999,-265]))#测试一下基本对了.强大的预处理数组技巧.美团的题目还是水平可以的.
'''
并查集:
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 = []
        self.distance = float('inf') #为了在下面单源最短路径问题.所以这里直接建立时候初始化到无穷.
    def __cmp__(self,other):
        return cmp(self.distance, other.distance)
    def __lt__(self,other):#operator <
        return self.distance < other.distance
    def __ge__(self,other):#oprator >=
        return self.distance >= other.distance
    def __gt__(self,other):#oprator >=
        return self.distance > other.distance
    def __le__(self,other):#oprator <=
        return self.distance <= other.distance
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就行
'''

#左神用的是栈,来直接模拟递归过程.好像跟递归写法差不多
def dfs_me(node): 
    visited = set([node])#set或者list自动是全局变量.在多少重子函数里面他都是能调用外面的list.
    def mini_dfs(node):
        
        print(node.val)#遍历的操作
        for i in node.nexts:
            if i not in visited:
               mini_dfs(i)
               visited.add(i)
    mini_dfs(node)
    return 
print('ceshi0')
dfs_me(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

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)
            
'''
慕课网课程:
自环边,平行边.有这2种边的叫复杂图.
我们上面左神的表达方法:就是邻接表.
'''
'''
单源最短路径:直接动态规划即可.很容易
显然满足最优子结构.a到b再到c的最短路径.一定是a到b的最短路径加上b到c的最短路径.
'''
'''
dijkstra 地理课死啦算法.前提:图中不能有负权边.
'''
#格式刷快捷键ctrl+k 再 ctrl+f
def dijkstra(graph,start):#为了记录路径直接在node里面加一个last_node属性即可.他记录了这个节点在最短路径
                    #这个过程里面他的上一步是哪个节点.
                                              #然后继续加一个distance属性,来记录这个节点他距离最开始节点的距离.
        
          #start是一个node对象.
        visited = set()
        pq = PQueue()
        pq_assist = set() #用来判断元素是否在pq中.pq加入元素他就加入元素,pq删除他就删除!
                       #总之我这么构造的原因就是为了避免重新修改heapq代码.和node类
        #初始化start点
        start.distance = 0
        start.last_node = start
        visited.add(start)

        #直接在优先队列里面存入node.只存入node即可.排序函数里面写distance即可.
        #修改时候直接修改node即可,对队列里面数值进行修改.
        pq.put(start)
        pq_assist.add(start)
        while pq.empty() != True:
            tmp = pq.get() #所以一开始这个tmp就是start
            pq_assist.remove(tmp)
            visited.add(tmp)
            #找tmp相邻的边.
            for i in tmp.edges:
                obj = i.to#obj是新节点v相连的to节点.
                if obj not in visited:
                    if tmp.distance + i.weight < obj.distance:
                        obj.distance = tmp.distance + i.weight#说是松弛操作,但是看起来非常像拉紧操作!他起名时脑袋进水了
                        obj.last_node = tmp
                    if obj not in pq_assist:
                        pq.put(obj)
                        pq_assist.add(obj)
a = PQueue()
a.put(3)
a.put(3)
a.put(3)
a.put(3)

#graph =
#GraphGenerator([[1,2,3],[2,4,5],[2,6,7],[4,6,5],[1,6,99],[99,98,999]])这个是测试用的图
dijkstra(graph,graph.nodes[1])
print(graph.nodes[6].last_node.val)
'''
总结一下我写的这个函数:
1.利用了辅助set技巧.来实现对队列里面元素的跟踪看他是否在队列中.
话说一个队列连一个in方法都没有,很坑!
2.设计最重要的部分,是因为python对于一个对象,如果把它放到任意容器中,你只要修改对象的属性,那么
在容器中的这个对象也自动修改.(同一个对象的本质应该是引用.)利用这点直接在队列外面修改对象属性
即可.不用管队列内部了.其实也没法访问队列内部,队列不能随机存取.用对象就避免了这个复杂操作.
从而提高时间效率.
3.对于最短路径的跟踪方法.也是用对象里面加入一个last_node属性.取值是一个node对象.
利用他就能查询最短路径的上一个节点是哪个节点了.
'''

'''
bellman ford算法:处理带负权的边的图的单元最短路径问题.比上面的distra方法更简单.但是效率慢非常多.
算法不需要辅助结构,就是一码for 循环即可.
'''
def bellmanford(graph,start):
        #初始化start点
        start.distance = 0 #初始化,也是下面判断负权环的本质.
        start.last_node = start
        #求|vertex|-1就是松弛的次数.
        times=len(graph.nodes.values())-1
        
        for i in range(times):
            for j in graph.edges:
                #做松弛操作
                weight=j.weight
                u=j.from1
                v=j.to
                if v.distance>u.distance+weight:
                    v.distance=u.distance+weight
                    v.last_node=u
        #判断是否有负权环
        for j in graph.edges:
                weight=j.weight
                u=j.from1
                v=j.to
                if v.distance>u.distance+weight:
                    return False
        return True
           
print('测试')
bellmanford(graph,graph.nodes[1])
print(graph.nodes[6].last_node.val)#效果不错
'''
总结:图的单元最短路径问题:
1.权都>=0:一定用特斯拉方法.因为他速度快
2.bellman方法为啥么一定要松弛|vertex|-1次呢?
因为每一个点无法用自己把自己松弛了.只能用其他人把自己松弛了.所以至多上面这么多次.才合理
但是如果有负权圈.那么还会继续下降.
原因就是start这个点的0会发生变化,负权图会把0给降低.这显然就不符合最短路径了.所以直接return False

'''
View Code

 

dijstrat算法改过来了.上面的自己乱想的不对.还是用索引堆来实现.看懂确实很麻烦,推荐liubobo讲的c++算法与数据结构(慕课网)里面有具体的c++实现.

'''
索引堆   通过liubobo老师的c++算法与数据结构  (慕课网)
         结构还是很复杂的,当然比dat和红黑树要简单多了.通过2个辅助数组来实现的
         如果看不懂可以参考上面的视频课程的第4章的内容.里面讲解很详细,我只是把c++代码修改
         成了python而已.非常强大,比如在图论中经常需要维护一个可以随意修改里面元素的堆结构.
         这时候索引堆就非常管用了.最短路径特斯拉算法,liubobo就是这么实现的.
'''
'''

实现使用2个辅助数组来做.有点像dat.用哈希表来做修改不行,只是能找到这个索引,而需要change操作
还是需要自己手动写.所以只能用双数组实现.

#引入索引堆的核心就是为了改变堆里面任意一个元素的值,然后继续维护这个堆.
'''


'''下面手动写堆'''
'''做大根堆然后输出升序排列'''#感觉之前写的都不对,heapify太弱了,不能按方向调整.
#需要修改成带shift up,shift down操作的堆,最终目标实现双辅助数组的最大索引堆

'''
下面我们把这个堆改成索引堆,叫index_max_heap
初始化时候.把数据这个list给index_max_heap对象里面的data这个属性是隐含的数据
同时索引index数组,是暴露给用户访问的.data[i]=item,index[count+1]=i
然后,我们之后的插入操作是插入索引为i,内容为item的元素,

总之:我们用索引i来替换item来进行堆里面的swap操作,然后最后堆里面第一个位置存index[0],表示
存的是data[index[0]].也就是index[i]表示的是堆里面第i个位置在data数据集里面的下表.(说起来很绕).
堆里面第几个元素就去index[几]里面找钥匙.然后去data[钥匙]里面去提取内容.
'''
class Maxheap():
    def __init__(self,capacity):
        self.data=[0]*capacity  #这个默认插入0非常不好,比如我索引只插入了1,'wo'2,'we'
                               #但是堆里面还是有10个元素.但是你如果不插入元素的画,
                               #你建立堆时候给的元素少了会发生bug.没法比较进行堆维护.
                               #所以使用的时候一定要注意,用多少capacity就给多少.
                               #如果需要占位的时候,空余的占位要自己补上default值.

                               #如果能动态的赋予堆空间就好了!
        self.indexes=[-1]*capacity #因为索引不可能是负的,所以用-1占位
        self.reverse=[-1]*capacity #因为索引不可能是负的,所以用-1占位
        self.count=0
        self.capacity=capacity
    def size(self):
        return self.count
    def empty(self):
        return self.count==0

    def shiftup(self,count):
        while count>0 and self.data[self.indexes[(count-1)//2]]
            
            self.indexes[(count-1)//2],self.indexes[count]=self.indexes[count],self.indexes[(count-1)//2]#这一步只是交换index提高了交换效率
            #下面2行是公里,因为上面变了,所以下面需要跑一下这2行.坐下对应修复
            self.reverse[self.indexes[(count-1)//2]]=(count-1)//2
            self.reverse[self.indexes[count]]=count

            count=(count-1)//2

    def shiftdown(self,k):#把堆的k索引的元素进行shiftdown操作,
                          #每一次这个操作都能把k位置作为head的子树给heapify了.
        while 2*k+1<=self.count-1 :
           left=2*k+1
           right=min(2*k+2,self.count-1)
           tmpindex=k
           if self.data[self.indexes[left]]>self.data[self.indexes[k]]:
               tmpindex=left
           if self.data[self.indexes[right]]>self.data[self.indexes[tmpindex]]:
               tmpindex=right
           if tmpindex==k:
               return 
           else:
               self.indexes[tmpindex],self.indexes[k]=self.indexes[k],self.indexes[tmpindex]
               self.reverse[self.indexes[tmpindex]]=tmpindex
               self.reverse[self.indexes[k]]=k
               k=tmpindex


        

               #插入索引为i的数据是item
    def insert(self,i,item):#建立只需要shift up
        assert(self.count+1<=self.capacity)
        assert(i>=0 and i<self.capacity)
        self.data[i]=item
        self.indexes[self.count]=i
        self.reverse[i]=self.count
        
        
        self.shiftup(self.count)#把count位置的元素向上移动维护堆
        self.count+=1


    def pop(self):#弹出堆定元素
        assert(self.count>0)
        self.indexes[0],self.indexes[self.count-1]=self.indexes[self.count-1],self.indexes[0]
        self.reverse[self.indexes[0]]=0
        self.reverse[self.indexes[self.count-1]]=-1#pop就不会被访问了所以给-1

        output=self.data[self.indexes[self.count-1]]
        self.count-=1
        if self.count>0:
         self.shiftdown(0)#把索引为0的进行shiftdown操作
        return output


    def pop_index(self):#弹出堆定元素
        assert(self.count>0)
        self.indexes[0],self.indexes[self.count-1]=self.indexes[self.count-1],self.indexes[0]
        self.reverse[self.indexes[0]]=0
        self.reverse[self.indexes[self.count-1]]=-1#pop就不会被访问了所以给-1
        output=self.indexes[self.count-1]
        self.count-=1
        if self.count>0:
         self.shiftdown(0)#把索引为0的进行shiftdown操作
        return output

    def show_data(self):#因为堆,需要不动态删除,为了速度.所以不要的元素只是把它放到count-1 这个
                      #index后面而已,通过show_data来读取data中有效元素
                      #利用index来遍历更准确和不会bug
        out=[]
        index_now=self.indexes[:self.size()]
        for i in index_now:
            if i!=-1:
                out.append(self.data[i])
        out

        return out
    def heapify(self,list1):#把数组直接建立成一个最大堆
        self.data=list1#python的列表动态的,直接赋值即可.不用管capacity
        self.capacity=len(list1)
        self.count=self.capacity
        for i in range((self.capacity-2)//2,-1,-1):
            self.shiftdown(i)
        return self.data
    def heapsort(self,list1):#直接pop 就实现了.因为前面都已经写好了
        self.heapify(list1)
        while self.count>1:
         self.pop()#弹出一个

        return self.data
    def get_item(self,i):
        return self.data[i]
    def change(self,i,newitem):#需要返回索引i在堆中的第几个坐标上.比如堆中第一个元素是10,那么change(10)返回0
        #这种操作,叫反向查找技术,非常牛逼class,实现不难,思想牛逼.常用.思想很像dat
        #先修改data
        self.data[i]=newitem
        j=self.reverse[i]
        self.shiftup(j)
        self.shiftdown(j)


        return self.data[i]



#下面是测试
print('测试大index堆')
a=Maxheap(10)

a.insert(0,'wo')#0是索引,'wo'是value.对value比较大小来建堆,但是堆里面的元素都是index.
a.insert(1,'we')

a.insert(3,'a')
a.change(3,'jjk')
print(a.pop_index())
print(a.indexes)#弹出的元素不会彻底删除,而只是把它的索引放到self.size后面了.
print(a.show_data()) #从这里面就看出来之前最大的wo已经被弹出了.
                     #并且indexMaxheap也已经效果出来了.可以随意修改index为3的元素了,并且
                     #自动维护这个堆.
            






#然而图论需要的是minheap.我改成indexminheap,特斯拉算法需要
#capacity是最大容量#是最小堆所以加入无穷做初始化,动态做data可以自己实现时候直接修改data的实现方式.
class indexminheap():
    def __init__(self,capacity):#capacity是最大容量
        self.data=[float('inf')]*capacity  #这个默认插入0非常不好,比如我索引只插入了1,'wo'2,'we'
                               #但是堆里面还是有10个元素.但是你如果不插入元素的画,
                               #你建立堆时候给的元素少了会发生bug.没法比较进行堆维护.
                               #所以使用的时候一定要注意,用多少capacity就给多少.
                               #如果需要占位的时候,空余的占位要自己补上default值.
        self.indexes=[-1]*capacity #因为索引不可能是负的,所以用-1占位
        self.reverse=[-1]*capacity #因为索引不可能是负的,所以用-1占位
        self.count=0
        self.capacity=capacity
    def size(self):
        return self.count
    def empty(self):
        return self.count==0

    def shiftup(self,count):
        while count>0 and self.data[self.indexes[(count-1)//2]]>self.data[self.indexes[count]]:
            
            self.indexes[(count-1)//2],self.indexes[count]=self.indexes[count],self.indexes[(count-1)//2]#这一步只是交换index提高了交换效率
            #下面2行是公里,因为上面变了,所以下面需要跑一下这2行.坐下对应修复
            self.reverse[self.indexes[(count-1)//2]]=(count-1)//2
            self.reverse[self.indexes[count]]=count

            count=(count-1)//2

    def shiftdown(self,k):#把堆的k索引的元素进行shiftdown操作,
                          #每一次这个操作都能把k位置作为head的子树给heapify了.
        while 2*k+1<=self.count-1 :
           left=2*k+1
           right=min(2*k+2,self.count-1)
           tmpindex=k
           if self.data[self.indexes[left]]<self.data[self.indexes[k]]:
               tmpindex=left
           if self.data[self.indexes[right]]<self.data[self.indexes[tmpindex]]:
               tmpindex=right
           if tmpindex==k:
               return 
           else:
               self.indexes[tmpindex],self.indexes[k]=self.indexes[k],self.indexes[tmpindex]
               self.reverse[self.indexes[tmpindex]]=tmpindex
               self.reverse[self.indexes[k]]=k
               k=tmpindex


        

               #插入索引为i的数据是item
    def insert(self,i,item):#建立只需要shift up
        assert(self.count+1<=self.capacity)
        assert(i>=0 and i<self.capacity)
        self.data[i]=item
        self.indexes[self.count]=i
        self.reverse[i]=self.count
        
        
        self.shiftup(self.count)#把count位置的元素向上移动维护堆
        self.count+=1


    def pop(self):#弹出堆定元素
        assert(self.count>0)
        self.indexes[0],self.indexes[self.count-1]=self.indexes[self.count-1],self.indexes[0]
        self.reverse[self.indexes[0]]=0
        self.reverse[self.indexes[self.count-1]]=-1#pop就不会被访问了所以给-1

        output=self.data[self.indexes[self.count-1]]
        self.count-=1
        if self.count>0:
         self.shiftdown(0)#把索引为0的进行shiftdown操作
        return output


    def pop_index(self):#弹出堆定元素
        assert(self.count>0)
        self.indexes[0],self.indexes[self.count-1]=self.indexes[self.count-1],self.indexes[0]
        self.reverse[self.indexes[0]]=0
        self.reverse[self.indexes[self.count-1]]=-1#pop就不会被访问了所以给-1
        output=self.indexes[self.count-1]
        self.count-=1
        if self.count>0:
         self.shiftdown(0)#把索引为0的进行shiftdown操作
        return output

    def show_data(self):#因为堆,需要不动态删除,为了速度.所以不要的元素只是把它放到count-1 这个
                      #index后面而已,通过show_data来读取data中有效元素
                      #利用index来遍历更准确和不会bug
        out=[]
        index_now=self.indexes[:self.size()]
        for i in index_now:
            if i!=-1:
                out.append(self.data[i])
        out

        return out
    def heapify(self,list1):#把数组直接建立成一个最大堆
        self.data=list1#python的列表动态的,直接赋值即可.不用管capacity
        self.capacity=len(list1)
        self.count=self.capacity
        for i in range((self.capacity-2)//2,-1,-1):
            self.shiftdown(i)
        return self.data
    def heapsort(self,list1):#直接pop 就实现了.因为前面都已经写好了
        self.heapify(list1)
        while self.count>1:
         self.pop()#弹出一个

        return self.data
    def get_item(self,i):
        return self.data[i]
    def change(self,i,newitem):#需要返回索引i在堆中的第几个坐标上.比如堆中第一个元素是10,那么change(10)返回0
        #这种操作,叫反向查找技术,非常牛逼class,实现不难,思想牛逼.常用.思想很像dat
        #先修改data
        self.data[i]=newitem
        j=self.reverse[i]
        self.shiftup(j)
        self.shiftdown(j)


        return self.data[i]


print('测试小index堆')
#下面是测试
a=indexminheap(10)

a.insert(0,'wo')#0是索引,'wo'是value.对value比较大小来建堆,但是堆里面的元素都是index.
a.insert(1,'we')

a.insert(3,'a')
a.change(3,'jjk')
print(a.pop_index())
print(a.indexes)#弹出的元素不会彻底删除,而只是把它的索引放到self.size后面了.
print(a.show_data()) #从这里面就看出来之前最大的wo已经被弹出了.
                     #并且indexMaxheap也已经效果出来了.可以随意修改index为3的元素了,并且
                     #自动维护这个堆.
            

'''
并查集:
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 = []
        self.distance = float('inf') #为了在下面单源最短路径问题.所以这里直接建立时候初始化到无穷.
    def __cmp__(self,other):
        return cmp(self.distance, other.distance)
    def __lt__(self,other):#operator <
        return self.distance < other.distance
    def __ge__(self,other):#oprator >=
        return self.distance >= other.distance
    def __gt__(self,other):#oprator >=
        return self.distance > other.distance
    def __le__(self,other):#oprator <=
        return self.distance <= other.distance
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就行
'''

#左神用的是栈,来直接模拟递归过程.好像跟递归写法差不多
def dfs_me(node): 
    visited = set([node])#set或者list自动是全局变量.在多少重子函数里面他都是能调用外面的list.
    def mini_dfs(node):
        
        print(node.val)#遍历的操作
        for i in node.nexts:
            if i not in visited:
               mini_dfs(i)
               visited.add(i)
    mini_dfs(node)
    return 
print('ceshi0')
dfs_me(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

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)
            
'''
慕课网课程:
自环边,平行边.有这2种边的叫复杂图.
我们上面左神的表达方法:就是邻接表.
'''
'''
单源最短路径:直接动态规划即可.很容易
显然满足最优子结构.a到b再到c的最短路径.一定是a到b的最短路径加上b到c的最短路径.
'''
'''
dijkstra 地理课死啦算法.前提:图中不能有负权边.
'''
#格式刷快捷键ctrl+k 再 ctrl+f
def dijkstra(graph,start):#为了记录路径直接在node里面加一个last_node属性即可.他记录了这个节点在最短路径
                    #这个过程里面他的上一步是哪个节点.
                                              #然后继续加一个distance属性,来记录这个节点他距离最开始节点的距离.
        
          #start是一个node对象.
        visited = set()
        capacity=len(graph.nodes.values())
        pq = indexminheap(capacity+1)
        #队列里面的东西是index是node.val data里面是node.distance这样就可以用索引堆来实现了





        #初始化start点
        start.distance = 0
        start.last_node = start
        visited.add(start)

        #直接在优先队列里面存入node.只存入node即可.排序函数里面写distance即可.
        #修改时候直接修改node即可,对队列里面数值进行修改.
        pq.insert(start.val,start.distance)

        while pq.empty() != True:
            
            tmp = pq.pop_index() #所以一开始这个tmp就是start
            if tmp==-1:#表示弹到default值了,根本不是边了,边都处理完了
                return 
            tmp=graph.nodes[tmp] #value转node

            visited.add(tmp)
            #找tmp相邻的边.
            for i in tmp.edges:
                obj = i.to#obj是新节点v相连的to节点.
                if obj not in visited:
                    if tmp.distance + i.weight < obj.distance:
                        pq.change(obj.val,tmp.distance + i.weight)
                        obj.distance = tmp.distance + i.weight

                        #说是松弛操作,但是看起来非常像拉紧操作!他起名时脑袋进水了
                        '''
                       !!!!!!!!!!!!!这个地方好像有问题,虽然队列里面内容直接修改了,但是队列里面
                       自动会维护最小堆性质么?应该不自动维护最小堆性质.
                       但是为什么测试了2个例子全对?难道他自动维护堆了?
                        '''

                        obj.last_node = tmp
                    if obj.val not in pq.indexes:
                        pq.insert(obj.val,obj.distance)


#graph =
#GraphGenerator([[1,2,3],[2,4,5],[2,6,7],[4,6,5],[1,6,99],[99,98,999]])这个是测试用的图
print('ceshi特斯拉开始')
graph = GraphGenerator([[1,2,10],[1,4,5],[2,3,1],[2,4,2],[3,5,4],[4,2,3],[4,3,9],[4,5,2],[5,1,7]
                        ,[5,3,6]])
dijkstra(graph,graph.nodes[1])
print(graph.nodes[5].last_node.val)#效果不错,返回2节点的last_node,也就是最短路径下的2的上一个节点
print('测试特斯拉结束,图论最难的算法')
'''
经过图论的学习.越来越发现莫装逼,装逼招雷劈,现有的算法能掌握好久已经不错了.
并查集,索引堆,哈希表,set,图论算法.能掌握熟练就已经很难了.像我之前写的用辅助set来替代索引堆果断打脸
幸好及时改过来了,毕竟半个多世纪的大神们发明的算法不是随便你一想就能做优化的.没几十年创造不了新算法.
'''
'''
总结一下我写的这个函数:
1.利用了辅助set技巧.来实现对队列里面元素的跟踪看他是否在队列中.
话说一个队列连一个in方法都没有,很坑!
2.设计最重要的部分,是因为python对于一个对象,如果把它放到任意容器中,你只要修改对象的属性,那么
在容器中的这个对象也自动修改.(同一个对象的本质应该是引用.)利用这点直接在队列外面修改对象属性
即可.不用管队列内部了.其实也没法访问队列内部,队列不能随机存取.用对象就避免了这个复杂操作.
从而提高时间效率.
3.对于最短路径的跟踪方法.也是用对象里面加入一个last_node属性.取值是一个node对象.
利用他就能查询最短路径的上一个节点是哪个节点了.
'''

'''
bellman ford算法:处理带负权的边的图的单元最短路径问题.比上面的distra方法更简单.但是效率慢非常多.
算法不需要辅助结构,就是一码for 循环即可.
'''
def bellmanford(graph,start):
        #初始化start点
        start.distance = 0 #初始化,也是下面判断负权环的本质.
        start.last_node = start
        #求|vertex|-1就是松弛的次数.
        times=len(graph.nodes.values())-1
        
        for i in range(times):
            for j in graph.edges:
                #做松弛操作
                weight=j.weight
                u=j.from1
                v=j.to
                if v.distance>u.distance+weight:
                    v.distance=u.distance+weight
                    v.last_node=u
        #判断是否有负权环
        for j in graph.edges:
                weight=j.weight
                u=j.from1
                v=j.to
                if v.distance>u.distance+weight:
                    return False
        return True
           
print('测试')
bellmanford(graph,graph.nodes[1])

'''
总结:图的单元最短路径问题:
1.权都>=0:一定用特斯拉方法.因为他速度快
2.bellman方法为啥么一定要松弛|vertex|-1次呢?
因为每一个点无法用自己把自己松弛了.只能用其他人把自己松弛了.所以至多上面这么多次.才合理
但是如果有负权圈.那么还会继续下降.
原因就是start这个点的0会发生变化,负权图会把0给降低.这显然就不符合最短路径了.所以直接return False

'''
#下面测试优先队列里面修改后是否优先队列还能自动维护堆的性质.
class node():
    def __init__(self,val):
     self.distance=val
    def __cmp__(self,other):
        return cmp(self.distance, other.distance)
    def __lt__(self,other):#operator <
        return self.distance < other.distance
    def __ge__(self,other):#oprator >=
        return self.distance >= other.distance
    def __gt__(self,other):#oprator >=
        return self.distance > other.distance
    def __le__(self,other):#oprator <=
        return self.distance <= other.distance
pq = PQueue()
a=node(111)
b=node(2)
c=node(3)
pq.put(a)
pq.put(b)
pq.put(c)
a.distance=999999999999
print(pq.get().distance)#结果显然不维护.所以上面我写的特斯拉算法是错误的,还是需要索引堆来实现.
View Code

 

'''
索引堆   通过liubobo老师的c++算法与数据结构  (慕课网)
         结构还是很复杂的,当然比dat和红黑树要简单多了.通过2个辅助数组来实现的
         如果看不懂可以参考上面的视频课程的第4章的内容.里面讲解很详细,我只是把c++代码修改
         成了python而已.非常强大,比如在图论中经常需要维护一个可以随意修改里面元素的堆结构.
         这时候索引堆就非常管用了.最短路径特斯拉算法,liubobo就是这么实现的.
'''
'''

实现使用2个辅助数组来做.有点像dat.用哈希表来做修改不行,只是能找到这个索引,而需要change操作
还是需要自己手动写.所以只能用双数组实现.

#引入索引堆的核心就是为了改变堆里面任意一个元素的值,然后继续维护这个堆.
'''


'''下面手动写堆'''
'''做大根堆然后输出升序排列'''#感觉之前写的都不对,heapify太弱了,不能按方向调整.
#需要修改成带shift up,shift down操作的堆,最终目标实现双辅助数组的最大索引堆

'''
下面我们把这个堆改成索引堆,叫index_max_heap
初始化时候.把数据这个list给index_max_heap对象里面的data这个属性是隐含的数据
同时索引index数组,是暴露给用户访问的.data[i]=item,index[count+1]=i
然后,我们之后的插入操作是插入索引为i,内容为item的元素,

总之:我们用索引i来替换item来进行堆里面的swap操作,然后最后堆里面第一个位置存index[0],表示
存的是data[index[0]].也就是index[i]表示的是堆里面第i个位置在data数据集里面的下表.(说起来很绕).
堆里面第几个元素就去index[几]里面找钥匙.然后去data[钥匙]里面去提取内容.
'''
class Maxheap():
    def __init__(self,capacity):
        self.data=[0]*capacity  #这个默认插入0非常不好,比如我索引只插入了1,'wo'2,'we'
                               #但是堆里面还是有10个元素.但是你如果不插入元素的画,
                               #你建立堆时候给的元素少了会发生bug.没法比较进行堆维护.
                               #所以使用的时候一定要注意,用多少capacity就给多少.
                               #如果需要占位的时候,空余的占位要自己补上default值.

                               #如果能动态的赋予堆空间就好了!
        self.indexes=[-1]*capacity #因为索引不可能是负的,所以用-1占位
        self.reverse=[-1]*capacity #因为索引不可能是负的,所以用-1占位
        self.count=0
        self.capacity=capacity
    def size(self):
        return self.count
    def empty(self):
        return self.count==0

    def shiftup(self,count):
        while count>0 and self.data[self.indexes[(count-1)//2]]
            
            self.indexes[(count-1)//2],self.indexes[count]=self.indexes[count],self.indexes[(count-1)//2]#这一步只是交换index提高了交换效率
            #下面2行是公里,因为上面变了,所以下面需要跑一下这2行.坐下对应修复
            self.reverse[self.indexes[(count-1)//2]]=(count-1)//2
            self.reverse[self.indexes[count]]=count

            count=(count-1)//2

    def shiftdown(self,k):#把堆的k索引的元素进行shiftdown操作,
                          #每一次这个操作都能把k位置作为head的子树给heapify了.
        while 2*k+1<=self.count-1 :
           left=2*k+1
           right=min(2*k+2,self.count-1)
           tmpindex=k
           if self.data[self.indexes[left]]>self.data[self.indexes[k]]:
               tmpindex=left
           if self.data[self.indexes[right]]>self.data[self.indexes[tmpindex]]:
               tmpindex=right
           if tmpindex==k:
               return 
           else:
               self.indexes[tmpindex],self.indexes[k]=self.indexes[k],self.indexes[tmpindex]
               self.reverse[self.indexes[tmpindex]]=tmpindex
               self.reverse[self.indexes[k]]=k
               k=tmpindex


        

               #插入索引为i的数据是item
    def insert(self,i,item):#建立只需要shift up
        assert(self.count+1<=self.capacity)
        assert(i>=0 and i<self.capacity)
        self.data[i]=item
        self.indexes[self.count]=i
        self.reverse[i]=self.count
        
        
        self.shiftup(self.count)#把count位置的元素向上移动维护堆
        self.count+=1


    def pop(self):#弹出堆定元素
        assert(self.count>0)
        self.indexes[0],self.indexes[self.count-1]=self.indexes[self.count-1],self.indexes[0]
        self.reverse[self.indexes[0]]=0
        self.reverse[self.indexes[self.count-1]]=-1#pop就不会被访问了所以给-1

        output=self.data[self.indexes[self.count-1]]
        self.count-=1
        if self.count>0:
         self.shiftdown(0)#把索引为0的进行shiftdown操作
        return output


    def pop_index(self):#弹出堆定元素
        assert(self.count>0)
        self.indexes[0],self.indexes[self.count-1]=self.indexes[self.count-1],self.indexes[0]
        self.reverse[self.indexes[0]]=0
        self.reverse[self.indexes[self.count-1]]=-1#pop就不会被访问了所以给-1
        output=self.indexes[self.count-1]
        self.count-=1
        if self.count>0:
         self.shiftdown(0)#把索引为0的进行shiftdown操作
        return output

    def show_data(self):#因为堆,需要不动态删除,为了速度.所以不要的元素只是把它放到count-1 这个
                      #index后面而已,通过show_data来读取data中有效元素
                      #利用index来遍历更准确和不会bug
        out=[]
        index_now=self.indexes[:self.size()]
        for i in index_now:
            if i!=-1:
                out.append(self.data[i])
        out

        return out
    def heapify(self,list1):#把数组直接建立成一个最大堆
        self.data=list1#python的列表动态的,直接赋值即可.不用管capacity
        self.capacity=len(list1)
        self.count=self.capacity
        for i in range((self.capacity-2)//2,-1,-1):
            self.shiftdown(i)
        return self.data
    def heapsort(self,list1):#直接pop 就实现了.因为前面都已经写好了
        self.heapify(list1)
        while self.count>1:
         self.pop()#弹出一个

        return self.data
    def get_item(self,i):
        return self.data[i]
    def change(self,i,newitem):#需要返回索引i在堆中的第几个坐标上.比如堆中第一个元素是10,那么change(10)返回0
        #这种操作,叫反向查找技术,非常牛逼class,实现不难,思想牛逼.常用.思想很像dat
        #先修改data
        self.data[i]=newitem
        j=self.reverse[i]
        self.shiftup(j)
        self.shiftdown(j)


        return self.data[i]



#下面是测试
print('测试大index堆')
a=Maxheap(10)

a.insert(0,'wo')#0是索引,'wo'是value.对value比较大小来建堆,但是堆里面的元素都是index.
a.insert(1,'we')

a.insert(3,'a')
a.change(3,'jjk')
print(a.pop_index())
print(a.indexes)#弹出的元素不会彻底删除,而只是把它的索引放到self.size后面了.
print(a.show_data()) #从这里面就看出来之前最大的wo已经被弹出了.
                     #并且indexMaxheap也已经效果出来了.可以随意修改index为3的元素了,并且
                     #自动维护这个堆.
            






#然而图论需要的是minheap.我改成indexminheap,特斯拉算法需要
#capacity是最大容量#是最小堆所以加入无穷做初始化,动态做data可以自己实现时候直接修改data的实现方式.
class indexminheap():
    def __init__(self,capacity):#capacity是最大容量
        self.data=[float('inf')]*capacity  #这个默认插入0非常不好,比如我索引只插入了1,'wo'2,'we'
                               #但是堆里面还是有10个元素.但是你如果不插入元素的画,
                               #你建立堆时候给的元素少了会发生bug.没法比较进行堆维护.
                               #所以使用的时候一定要注意,用多少capacity就给多少.
                               #如果需要占位的时候,空余的占位要自己补上default值.
        self.indexes=[-1]*capacity #因为索引不可能是负的,所以用-1占位
        self.reverse=[-1]*capacity #因为索引不可能是负的,所以用-1占位
        self.count=0
        self.capacity=capacity
    def size(self):
        return self.count
    def empty(self):
        return self.count==0

    def shiftup(self,count):
        while count>0 and self.data[self.indexes[(count-1)//2]]>self.data[self.indexes[count]]:
            
            self.indexes[(count-1)//2],self.indexes[count]=self.indexes[count],self.indexes[(count-1)//2]#这一步只是交换index提高了交换效率
            #下面2行是公里,因为上面变了,所以下面需要跑一下这2行.坐下对应修复
            self.reverse[self.indexes[(count-1)//2]]=(count-1)//2
            self.reverse[self.indexes[count]]=count

            count=(count-1)//2

    def shiftdown(self,k):#把堆的k索引的元素进行shiftdown操作,
                          #每一次这个操作都能把k位置作为head的子树给heapify了.
        while 2*k+1<=self.count-1 :
           left=2*k+1
           right=min(2*k+2,self.count-1)
           tmpindex=k
           if self.data[self.indexes[left]]<self.data[self.indexes[k]]:
               tmpindex=left
           if self.data[self.indexes[right]]<self.data[self.indexes[tmpindex]]:
               tmpindex=right
           if tmpindex==k:
               return 
           else:
               self.indexes[tmpindex],self.indexes[k]=self.indexes[k],self.indexes[tmpindex]
               self.reverse[self.indexes[tmpindex]]=tmpindex
               self.reverse[self.indexes[k]]=k
               k=tmpindex


        

               #插入索引为i的数据是item
    def insert(self,i,item):#建立只需要shift up
        assert(self.count+1<=self.capacity)
        assert(i>=0 and i<self.capacity)
        self.data[i]=item
        self.indexes[self.count]=i
        self.reverse[i]=self.count
        
        
        self.shiftup(self.count)#把count位置的元素向上移动维护堆
        self.count+=1


    def pop(self):#弹出堆定元素
        assert(self.count>0)
        self.indexes[0],self.indexes[self.count-1]=self.indexes[self.count-1],self.indexes[0]
        self.reverse[self.indexes[0]]=0
        self.reverse[self.indexes[self.count-1]]=-1#pop就不会被访问了所以给-1

        output=self.data[self.indexes[self.count-1]]
        self.count-=1
        if self.count>0:
         self.shiftdown(0)#把索引为0的进行shiftdown操作
        return output


    def pop_index(self):#弹出堆定元素
        assert(self.count>0)
        self.indexes[0],self.indexes[self.count-1]=self.indexes[self.count-1],self.indexes[0]
        self.reverse[self.indexes[0]]=0
        self.reverse[self.indexes[self.count-1]]=-1#pop就不会被访问了所以给-1
        output=self.indexes[self.count-1]
        self.count-=1
        if self.count>0:
         self.shiftdown(0)#把索引为0的进行shiftdown操作
        return output

    def show_data(self):#因为堆,需要不动态删除,为了速度.所以不要的元素只是把它放到count-1 这个
                      #index后面而已,通过show_data来读取data中有效元素
                      #利用index来遍历更准确和不会bug
        out=[]
        index_now=self.indexes[:self.size()]
        for i in index_now:
            if i!=-1:
                out.append(self.data[i])
        out

        return out
    def heapify(self,list1):#把数组直接建立成一个最大堆
        self.data=list1#python的列表动态的,直接赋值即可.不用管capacity
        self.capacity=len(list1)
        self.count=self.capacity
        for i in range((self.capacity-2)//2,-1,-1):
            self.shiftdown(i)
        return self.data
    def heapsort(self,list1):#直接pop 就实现了.因为前面都已经写好了
        self.heapify(list1)
        while self.count>1:
         self.pop()#弹出一个

        return self.data
    def get_item(self,i):
        return self.data[i]
    def change(self,i,newitem):#需要返回索引i在堆中的第几个坐标上.比如堆中第一个元素是10,那么change(10)返回0
        #这种操作,叫反向查找技术,非常牛逼class,实现不难,思想牛逼.常用.思想很像dat
        #先修改data
        self.data[i]=newitem
        j=self.reverse[i]
        self.shiftup(j)
        self.shiftdown(j)


        return self.data[i]


print('测试小index堆')
#下面是测试
a=indexminheap(10)

a.insert(0,'wo')#0是索引,'wo'是value.对value比较大小来建堆,但是堆里面的元素都是index.
a.insert(1,'we')

a.insert(3,'a')
a.change(3,'jjk')
print(a.pop_index())
print(a.indexes)#弹出的元素不会彻底删除,而只是把它的索引放到self.size后面了.
print(a.show_data()) #从这里面就看出来之前最大的wo已经被弹出了.
                     #并且indexMaxheap也已经效果出来了.可以随意修改index为3的元素了,并且
                     #自动维护这个堆.
            

'''
并查集:
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 = []
        self.distance = float('inf') #为了在下面单源最短路径问题.所以这里直接建立时候初始化到无穷.
    def __cmp__(self,other):
        return cmp(self.distance, other.distance)
    def __lt__(self,other):#operator <
        return self.distance < other.distance
    def __ge__(self,other):#oprator >=
        return self.distance >= other.distance
    def __gt__(self,other):#oprator >=
        return self.distance > other.distance
    def __le__(self,other):#oprator <=
        return self.distance <= other.distance
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就行
'''

#左神用的是栈,来直接模拟递归过程.好像跟递归写法差不多
def dfs_me(node): 
    visited = set([node])#set或者list自动是全局变量.在多少重子函数里面他都是能调用外面的list.
    def mini_dfs(node):
        
        print(node.val)#遍历的操作
        for i in node.nexts:
            if i not in visited:
               mini_dfs(i)
               visited.add(i)
    mini_dfs(node)
    return 
print('ceshi0')
dfs_me(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

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)
            
'''
慕课网课程:
自环边,平行边.有这2种边的叫复杂图.
我们上面左神的表达方法:就是邻接表.
'''
'''
单源最短路径:直接动态规划即可.很容易
显然满足最优子结构.a到b再到c的最短路径.一定是a到b的最短路径加上b到c的最短路径.
'''
'''
dijkstra 地理课死啦算法.前提:图中不能有负权边.
'''
#格式刷快捷键ctrl+k 再 ctrl+f
def dijkstra(graph,start):#为了记录路径直接在node里面加一个last_node属性即可.他记录了这个节点在最短路径
                    #这个过程里面他的上一步是哪个节点.
                                              #然后继续加一个distance属性,来记录这个节点他距离最开始节点的距离.
        
          #start是一个node对象.
        visited = set()
        capacity=len(graph.nodes.values())
        pq = indexminheap(capacity+1)
        #队列里面的东西是index是node.val data里面是node.distance这样就可以用索引堆来实现了





        #初始化start点
        start.distance = 0
        start.last_node = start
        visited.add(start)

        #直接在优先队列里面存入node.只存入node即可.排序函数里面写distance即可.
        #修改时候直接修改node即可,对队列里面数值进行修改.
        pq.insert(start.val,start.distance)

        while pq.empty() != True:
            
            tmp = pq.pop_index() #所以一开始这个tmp就是start
            if tmp==-1:#表示弹到default值了,根本不是边了,边都处理完了
                return 
            tmp=graph.nodes[tmp] #value转node

            visited.add(tmp)
            #找tmp相邻的边.
            for i in tmp.edges:
                obj = i.to#obj是新节点v相连的to节点.
                if obj not in visited:
                    if tmp.distance + i.weight < obj.distance:
                        pq.change(obj.val,tmp.distance + i.weight)
                        obj.distance = tmp.distance + i.weight

                        #说是松弛操作,但是看起来非常像拉紧操作!他起名时脑袋进水了
                        '''
                       !!!!!!!!!!!!!这个地方好像有问题,虽然队列里面内容直接修改了,但是队列里面
                       自动会维护最小堆性质么?应该不自动维护最小堆性质.
                       但是为什么测试了2个例子全对?难道他自动维护堆了?
                        '''

                        obj.last_node = tmp
                    if obj.val not in pq.indexes:
                        pq.insert(obj.val,obj.distance)


#graph =
#GraphGenerator([[1,2,3],[2,4,5],[2,6,7],[4,6,5],[1,6,99],[99,98,999]])这个是测试用的图
print('ceshi特斯拉开始')
graph = GraphGenerator([[1,2,10],[1,4,5],[2,3,1],[2,4,2],[3,5,4],[4,2,3],[4,3,9],[4,5,2],[5,1,7]
                        ,[5,3,6]])
dijkstra(graph,graph.nodes[1])
print(graph.nodes[5].last_node.val)#效果不错,返回2节点的last_node,也就是最短路径下的2的上一个节点
print('测试特斯拉结束,图论最难的算法')
'''
经过图论的学习.越来越发现莫装逼,装逼招雷劈,现有的算法能掌握好久已经不错了.
并查集,索引堆,哈希表,set,图论算法.能掌握熟练就已经很难了.像我之前写的用辅助set来替代索引堆果断打脸
幸好及时改过来了,毕竟半个多世纪的大神们发明的算法不是随便你一想就能做优化的.没几十年创造不了新算法.
'''
'''
总结一下我写的这个函数:
1.利用了辅助set技巧.来实现对队列里面元素的跟踪看他是否在队列中.
话说一个队列连一个in方法都没有,很坑!
2.设计最重要的部分,是因为python对于一个对象,如果把它放到任意容器中,你只要修改对象的属性,那么
在容器中的这个对象也自动修改.(同一个对象的本质应该是引用.)利用这点直接在队列外面修改对象属性
即可.不用管队列内部了.其实也没法访问队列内部,队列不能随机存取.用对象就避免了这个复杂操作.
从而提高时间效率.
3.对于最短路径的跟踪方法.也是用对象里面加入一个last_node属性.取值是一个node对象.
利用他就能查询最短路径的上一个节点是哪个节点了.
'''

'''
bellman ford算法:处理带负权的边的图的单元最短路径问题.比上面的distra方法更简单.但是效率慢非常多.
算法不需要辅助结构,就是一码for 循环即可.
'''
def bellmanford(graph,start):
        #初始化start点
        start.distance = 0 #初始化,也是下面判断负权环的本质.
        start.last_node = start
        #求|vertex|-1就是松弛的次数.
        times=len(graph.nodes.values())-1
        
        for i in range(times):
            for j in graph.edges:
                #做松弛操作
                weight=j.weight
                u=j.from1
                v=j.to
                if v.distance>u.distance+weight:
                    v.distance=u.distance+weight
                    v.last_node=u
        #判断是否有负权环
        for j in graph.edges:
                weight=j.weight
                u=j.from1
                v=j.to
                if v.distance>u.distance+weight:
                    return False
        return True
           
print('测试')
bellmanford(graph,graph.nodes[1])

'''
总结:图的单元最短路径问题:
1.权都>=0:一定用特斯拉方法.因为他速度快
2.bellman方法为啥么一定要松弛|vertex|-1次呢?
因为每一个点无法用自己把自己松弛了.只能用其他人把自己松弛了.所以至多上面这么多次.才合理
但是如果有负权圈.那么还会继续下降.
原因就是start这个点的0会发生变化,负权图会把0给降低.这显然就不符合最短路径了.所以直接return False

'''
#下面测试优先队列里面修改后是否优先队列还能自动维护堆的性质.
class node():
    def __init__(self,val):
     self.distance=val
    def __cmp__(self,other):
        return cmp(self.distance, other.distance)
    def __lt__(self,other):#operator <
        return self.distance < other.distance
    def __ge__(self,other):#oprator >=
        return self.distance >= other.distance
    def __gt__(self,other):#oprator >=
        return self.distance > other.distance
    def __le__(self,other):#oprator <=
        return self.distance <= other.distance
pq = PQueue()
a=node(111)
b=node(2)
c=node(3)
pq.put(a)
pq.put(b)
pq.put(c)
a.distance=999999999999
print(pq.get().distance)#结果显然不维护.所以上面我写的特斯拉算法是错误的,还是需要索引堆来实现.
class node():
    def __init__(self,val):
     self.distance=val
    def __cmp__(self,other):
        return cmp(self.distance, other.distance)
    def __lt__(self,other):#operator <
        return self.distance < other.distance
    def __ge__(self,other):#oprator >=
        return self.distance >= other.distance
    def __gt__(self,other):#oprator >=
        return self.distance > other.distance
    def __le__(self,other):#oprator <=
        return self.distance <= other.distance
pq = indexminheap(999)
a=node(111)
b=node(2)
c=node(3)
pq.insert(1,a.distance)
pq.insert(2,b.distance)
pq.insert(3,c.distance)
pq.change(1,8000)
print(pq.pop())#用索引堆显然就得到了2.就是我们要的效果.用索引来修改堆中任意元素并且能维护堆
View Code

 扩充了floyd算法

#代码说明:实现了并查集,大根索引堆,小根索引堆,图的dfs,bfs,最小生成树:k算法,p算法
#单源最短路径:dijistras算法和bellman算法
#图的类建立:GraphGenerator(matrix) 把一个矩阵里面元素是[from,end,边长] 数据写到图里面
#floyd算法:计算图中任意两点的最短距离






'''
索引堆   通过liubobo老师的c++算法与数据结构  (慕课网)
         结构还是很复杂的,当然比dat和红黑树要简单多了.通过2个辅助数组来实现的
         如果看不懂可以参考上面的视频课程的第4章的内容.里面讲解很详细,我只是把c++代码修改
         成了python而已.非常强大,比如在图论中经常需要维护一个可以随意修改里面元素的堆结构.
         这时候索引堆就非常管用了.最短路径特斯拉算法,liubobo就是这么实现的.
'''
'''

实现使用2个辅助数组来做.有点像dat.用哈希表来做修改不行,只是能找到这个索引,而需要change操作
还是需要自己手动写.所以只能用双数组实现.

#引入索引堆的核心就是为了改变堆里面任意一个元素的值,然后继续维护这个堆.
'''


'''下面手动写堆'''
'''做大根堆然后输出升序排列'''#感觉之前写的都不对,heapify太弱了,不能按方向调整.
#需要修改成带shift up,shift down操作的堆,最终目标实现双辅助数组的最大索引堆

'''
下面我们把这个堆改成索引堆,叫index_max_heap
初始化时候.把数据这个list给index_max_heap对象里面的data这个属性是隐含的数据
同时索引index数组,是暴露给用户访问的.data[i]=item,index[count+1]=i
然后,我们之后的插入操作是插入索引为i,内容为item的元素,

总之:我们用索引i来替换item来进行堆里面的swap操作,然后最后堆里面第一个位置存index[0],表示
存的是data[index[0]].也就是index[i]表示的是堆里面第i个位置在data数据集里面的下表.(说起来很绕).
堆里面第几个元素就去index[几]里面找钥匙.然后去data[钥匙]里面去提取内容.
'''
class IndexMaxheap():
    def __init__(self,capacity):
        self.data=[0]*capacity  #这个默认插入0非常不好,比如我索引只插入了1,'wo'2,'we'
                               #但是堆里面还是有10个元素.但是你如果不插入元素的画,
                               #你建立堆时候给的元素少了会发生bug.没法比较进行堆维护.
                               #所以使用的时候一定要注意,用多少capacity就给多少.
                               #如果需要占位的时候,空余的占位要自己补上default值.

                               #如果能动态的赋予堆空间就好了!
        self.indexes=[-1]*capacity #因为索引不可能是负的,所以用-1占位
        self.reverse=[-1]*capacity #因为索引不可能是负的,所以用-1占位
        self.count=0
        self.capacity=capacity
    def size(self):
        return self.count
    def empty(self):
        return self.count==0

    def shiftup(self,count):
        while count>0 and self.data[self.indexes[(count-1)//2]]
            
            self.indexes[(count-1)//2],self.indexes[count]=self.indexes[count],self.indexes[(count-1)//2]#这一步只是交换index提高了交换效率
            #下面2行是公里,因为上面变了,所以下面需要跑一下这2行.坐下对应修复
            self.reverse[self.indexes[(count-1)//2]]=(count-1)//2
            self.reverse[self.indexes[count]]=count

            count=(count-1)//2

    def shiftdown(self,k):#把堆的k索引的元素进行shiftdown操作,
                          #每一次这个操作都能把k位置作为head的子树给heapify了.
        while 2*k+1<=self.count-1 :
           left=2*k+1
           right=min(2*k+2,self.count-1)
           tmpindex=k
           if self.data[self.indexes[left]]>self.data[self.indexes[k]]:
               tmpindex=left
           if self.data[self.indexes[right]]>self.data[self.indexes[tmpindex]]:
               tmpindex=right
           if tmpindex==k:
               return 
           else:
               self.indexes[tmpindex],self.indexes[k]=self.indexes[k],self.indexes[tmpindex]
               self.reverse[self.indexes[tmpindex]]=tmpindex
               self.reverse[self.indexes[k]]=k
               k=tmpindex


        

               #插入索引为i的数据是item
    def insert(self,i,item):#建立只需要shift up
        assert(self.count+1<=self.capacity)
        assert(i>=0 and i<self.capacity)
        self.data[i]=item
        self.indexes[self.count]=i
        self.reverse[i]=self.count
        
        
        self.shiftup(self.count)#把count位置的元素向上移动维护堆
        self.count+=1


    def pop(self):#弹出堆定元素
        assert(self.count>0)
        self.indexes[0],self.indexes[self.count-1]=self.indexes[self.count-1],self.indexes[0]
        self.reverse[self.indexes[0]]=0
        self.reverse[self.indexes[self.count-1]]=-1#pop就不会被访问了所以给-1

        output=self.data[self.indexes[self.count-1]]
        self.count-=1
        if self.count>0:
         self.shiftdown(0)#把索引为0的进行shiftdown操作
        return output


    def pop_index(self):#弹出堆定元素
        assert(self.count>0)
        self.indexes[0],self.indexes[self.count-1]=self.indexes[self.count-1],self.indexes[0]
        self.reverse[self.indexes[0]]=0
        self.reverse[self.indexes[self.count-1]]=-1#pop就不会被访问了所以给-1
        output=self.indexes[self.count-1]
        self.count-=1
        if self.count>0:
         self.shiftdown(0)#把索引为0的进行shiftdown操作
        return output

    def show_data(self):#因为堆,需要不动态删除,为了速度.所以不要的元素只是把它放到count-1 这个
                      #index后面而已,通过show_data来读取data中有效元素
                      #利用index来遍历更准确和不会bug
        out=[]
        index_now=self.indexes[:self.size()]
        for i in index_now:
            if i!=-1:
                out.append(self.data[i])
        out

        return out
    def heapify(self,list1):#把数组直接建立成一个最大堆
        self.data=list1#python的列表动态的,直接赋值即可.不用管capacity
        self.capacity=len(list1)
        self.count=self.capacity
        for i in range((self.capacity-2)//2,-1,-1):
            self.shiftdown(i)
        return self.data
    def heapsort(self,list1):#直接pop 就实现了.因为前面都已经写好了
        self.heapify(list1)
        while self.count>1:
         self.pop()#弹出一个

        return self.data
    def get_item(self,i):
        return self.data[i]
    def change(self,i,newitem):#需要返回索引i在堆中的第几个坐标上.比如堆中第一个元素是10,那么change(10)返回0
        #这种操作,叫反向查找技术,非常牛逼class,实现不难,思想牛逼.常用.思想很像dat
        #先修改data
        self.data[i]=newitem
        j=self.reverse[i]
        self.shiftup(j)
        self.shiftdown(j)


        return self.data[i]



#下面是测试
print('测试大index堆')
a=IndexMaxheap(10)

a.insert(0,'wo')#0是索引,'wo'是value.对value比较大小来建堆,但是堆里面的元素都是index.
a.insert(1,'we')

a.insert(3,'a')
a.change(3,'jjk')
print(a.pop_index())
print(a.indexes)#弹出的元素不会彻底删除,而只是把它的索引放到self.size后面了.
print(a.show_data()) #从这里面就看出来之前最大的wo已经被弹出了.
                     #并且indexMaxheap也已经效果出来了.可以随意修改index为3的元素了,并且
                     #自动维护这个堆.
            






#然而图论需要的是minheap.我改成indexminheap,特斯拉算法需要
#capacity是最大容量#是最小堆所以加入无穷做初始化,动态做data可以自己实现时候直接修改data的实现方式.
class indexminheap():
    def __init__(self,capacity):#capacity是最大容量
        self.data=[float('inf')]*capacity  #这个默认插入0非常不好,比如我索引只插入了1,'wo'2,'we'
                               #但是堆里面还是有10个元素.但是你如果不插入元素的画,
                               #你建立堆时候给的元素少了会发生bug.没法比较进行堆维护.
                               #所以使用的时候一定要注意,用多少capacity就给多少.
                               #如果需要占位的时候,空余的占位要自己补上default值.
        self.indexes=[-1]*capacity #因为索引不可能是负的,所以用-1占位
        self.reverse=[-1]*capacity #因为索引不可能是负的,所以用-1占位
        self.count=0
        self.capacity=capacity
    def size(self):
        return self.count
    def empty(self):
        return self.count==0

    def shiftup(self,count):
        while count>0 and self.data[self.indexes[(count-1)//2]]>self.data[self.indexes[count]]:
            
            self.indexes[(count-1)//2],self.indexes[count]=self.indexes[count],self.indexes[(count-1)//2]#这一步只是交换index提高了交换效率
            #下面2行是公里,因为上面变了,所以下面需要跑一下这2行.坐下对应修复
            self.reverse[self.indexes[(count-1)//2]]=(count-1)//2
            self.reverse[self.indexes[count]]=count

            count=(count-1)//2

    def shiftdown(self,k):#把堆的k索引的元素进行shiftdown操作,
                          #每一次这个操作都能把k位置作为head的子树给heapify了.
        while 2*k+1<=self.count-1 :
           left=2*k+1
           right=min(2*k+2,self.count-1)
           tmpindex=k
           if self.data[self.indexes[left]]<self.data[self.indexes[k]]:
               tmpindex=left
           if self.data[self.indexes[right]]<self.data[self.indexes[tmpindex]]:
               tmpindex=right
           if tmpindex==k:
               return 
           else:
               self.indexes[tmpindex],self.indexes[k]=self.indexes[k],self.indexes[tmpindex]
               self.reverse[self.indexes[tmpindex]]=tmpindex
               self.reverse[self.indexes[k]]=k
               k=tmpindex


        

               #插入索引为i的数据是item
    def insert(self,i,item):#建立只需要shift up
        assert(self.count+1<=self.capacity)
        assert(i>=0 and i<self.capacity)
        self.data[i]=item
        self.indexes[self.count]=i
        self.reverse[i]=self.count
        
        
        self.shiftup(self.count)#把count位置的元素向上移动维护堆
        self.count+=1


    def pop(self):#弹出堆定元素
        assert(self.count>0)
        self.indexes[0],self.indexes[self.count-1]=self.indexes[self.count-1],self.indexes[0]
        self.reverse[self.indexes[0]]=0
        self.reverse[self.indexes[self.count-1]]=-1#pop就不会被访问了所以给-1

        output=self.data[self.indexes[self.count-1]]
        self.count-=1
        if self.count>0:
         self.shiftdown(0)#把索引为0的进行shiftdown操作
        return output


    def pop_index(self):#弹出堆定元素
        assert(self.count>0)
        self.indexes[0],self.indexes[self.count-1]=self.indexes[self.count-1],self.indexes[0]
        self.reverse[self.indexes[0]]=0
        self.reverse[self.indexes[self.count-1]]=-1#pop就不会被访问了所以给-1
        output=self.indexes[self.count-1]
        self.count-=1
        if self.count>0:
         self.shiftdown(0)#把索引为0的进行shiftdown操作
        return output

    def show_data(self):#因为堆,需要不动态删除,为了速度.所以不要的元素只是把它放到count-1 这个
                      #index后面而已,通过show_data来读取data中有效元素
                      #利用index来遍历更准确和不会bug
        out=[]
        index_now=self.indexes[:self.size()]
        for i in index_now:
            if i!=-1:
                out.append(self.data[i])
        out

        return out
    def heapify(self,list1):#把数组直接建立成一个最大堆
        self.data=list1#python的列表动态的,直接赋值即可.不用管capacity
        self.capacity=len(list1)
        self.count=self.capacity
        for i in range((self.capacity-2)//2,-1,-1):
            self.shiftdown(i)
        return self.data
    def heapsort(self,list1):#直接pop 就实现了.因为前面都已经写好了
        self.heapify(list1)
        while self.count>1:
         self.pop()#弹出一个

        return self.data
    def get_item(self,i):
        return self.data[i]
    def change(self,i,newitem):#需要返回索引i在堆中的第几个坐标上.比如堆中第一个元素是10,那么change(10)返回0
        #这种操作,叫反向查找技术,非常牛逼class,实现不难,思想牛逼.常用.思想很像dat
        #先修改data
        self.data[i]=newitem
        j=self.reverse[i]
        self.shiftup(j)
        self.shiftdown(j)


        return self.data[i]


print('测试小index堆')
#下面是测试
a=indexminheap(10)

a.insert(0,'wo')#0是索引,'wo'是value.对value比较大小来建堆,但是堆里面的元素都是index.
a.insert(1,'we')

a.insert(3,'a')
a.change(3,'jjk')
print(a.pop_index())
print(a.indexes)#弹出的元素不会彻底删除,而只是把它的索引放到self.size后面了.
print(a.show_data()) #从这里面就看出来之前最大的wo已经被弹出了.
                     #并且indexMaxheap也已经效果出来了.可以随意修改index为3的元素了,并且
                     #自动维护这个堆.
            

'''
并查集:
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 = []
        self.distance = float('inf') #为了在下面单源最短路径问题.所以这里直接建立时候初始化到无穷.
    def __cmp__(self,other):
        return cmp(self.distance, other.distance)
    def __lt__(self,other):#operator <
        return self.distance < other.distance
    def __ge__(self,other):#oprator >=
        return self.distance >= other.distance
    def __gt__(self,other):#oprator >=
        return self.distance > other.distance
    def __le__(self,other):#oprator <=
        return self.distance <= other.distance
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就行
'''

#左神用的是栈,来直接模拟递归过程.好像跟递归写法差不多
def dfs_me(node): 
    visited = set([node])#set或者list自动是全局变量.在多少重子函数里面他都是能调用外面的list.
    def mini_dfs(node):
        
        print(node.val)#遍历的操作
        for i in node.nexts:
            if i not in visited:
               mini_dfs(i)
               visited.add(i)
    mini_dfs(node)
    return 
print('ceshi0')
dfs_me(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

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)
            
'''
慕课网课程:
自环边,平行边.有这2种边的叫复杂图.
我们上面左神的表达方法:就是邻接表.
'''
'''
单源最短路径:直接动态规划即可.很容易
显然满足最优子结构.a到b再到c的最短路径.一定是a到b的最短路径加上b到c的最短路径.
'''
'''
dijkstra 地理课死啦算法.前提:图中不能有负权边.
'''
#格式刷快捷键ctrl+k 再 ctrl+f
def dijkstra(graph,start):#为了记录路径直接在node里面加一个last_node属性即可.他记录了这个节点在最短路径
                    #这个过程里面他的上一步是哪个节点.
                                              #然后继续加一个distance属性,来记录这个节点他距离最开始节点的距离.
        
          #start是一个node对象.
        visited = set()
        capacity=len(graph.nodes.values())
        pq = indexminheap(capacity+1)
        #队列里面的东西是index是node.val data里面是node.distance这样就可以用索引堆来实现了





        #初始化start点
        start.distance = 0
        start.last_node = start
        visited.add(start)

        #直接在优先队列里面存入node.只存入node即可.排序函数里面写distance即可.
        #修改时候直接修改node即可,对队列里面数值进行修改.
        pq.insert(start.val,start.distance)

        while pq.empty() != True:
            
            tmp = pq.pop_index() #所以一开始这个tmp就是start
            if tmp==-1:#表示弹到default值了,根本不是边了,边都处理完了
                return 
            tmp=graph.nodes[tmp] #value转node

            visited.add(tmp)
            #找tmp相邻的边.
            for i in tmp.edges:
                obj = i.to#obj是新节点v相连的to节点.
                if obj not in visited:
                    if tmp.distance + i.weight < obj.distance:
                        pq.change(obj.val,tmp.distance + i.weight)
                        obj.distance = tmp.distance + i.weight

                        #说是松弛操作,但是看起来非常像拉紧操作!他起名时脑袋进水了
                        '''
                       !!!!!!!!!!!!!这个地方好像有问题,虽然队列里面内容直接修改了,但是队列里面
                       自动会维护最小堆性质么?应该不自动维护最小堆性质.
                       但是为什么测试了2个例子全对?难道他自动维护堆了?
                        '''

                        obj.last_node = tmp
                    if obj.val not in pq.indexes:
                        pq.insert(obj.val,obj.distance)


#graph =
#GraphGenerator([[1,2,3],[2,4,5],[2,6,7],[4,6,5],[1,6,99],[99,98,999]])这个是测试用的图
print('ceshi特斯拉开始')
graph = GraphGenerator([[1,2,10],[1,4,5],[2,3,1],[2,4,2],[3,5,4],[4,2,3],[4,3,9],[4,5,2],[5,1,7]
                        ,[5,3,6]])
dijkstra(graph,graph.nodes[1])
print(graph.nodes[5].last_node.val)#效果不错,返回2节点的last_node,也就是最短路径下的2的上一个节点
print('测试特斯拉结束,图论最难的算法')
'''
经过图论的学习.越来越发现莫装逼,装逼招雷劈,现有的算法能掌握好久已经不错了.
并查集,索引堆,哈希表,set,图论算法.能掌握熟练就已经很难了.像我之前写的用辅助set来替代索引堆果断打脸
幸好及时改过来了,毕竟半个多世纪的大神们发明的算法不是随便你一想就能做优化的.没几十年创造不了新算法.
'''
'''
总结一下我写的这个函数:
1.利用了辅助set技巧.来实现对队列里面元素的跟踪看他是否在队列中.
话说一个队列连一个in方法都没有,很坑!
2.设计最重要的部分,是因为python对于一个对象,如果把它放到任意容器中,你只要修改对象的属性,那么
在容器中的这个对象也自动修改.(同一个对象的本质应该是引用.)利用这点直接在队列外面修改对象属性
即可.不用管队列内部了.其实也没法访问队列内部,队列不能随机存取.用对象就避免了这个复杂操作.
从而提高时间效率.
3.对于最短路径的跟踪方法.也是用对象里面加入一个last_node属性.取值是一个node对象.
利用他就能查询最短路径的上一个节点是哪个节点了.
'''

'''
bellman ford算法:处理带负权的边的图的单元最短路径问题.比上面的distra方法更简单.但是效率慢非常多.
算法不需要辅助结构,就是一码for 循环即可.
'''
def bellmanford(graph,start):
        #初始化start点
        start.distance = 0 #初始化,也是下面判断负权环的本质.
        start.last_node = start
        #求|vertex|-1就是松弛的次数.
        times=len(graph.nodes.values())-1
        
        for i in range(times):
            for j in graph.edges:
                #做松弛操作
                weight=j.weight
                u=j.from1
                v=j.to
                if v.distance>u.distance+weight:
                    v.distance=u.distance+weight
                    v.last_node=u
        #判断是否有负权环
        for j in graph.edges:
                weight=j.weight
                u=j.from1
                v=j.to
                if v.distance>u.distance+weight:
                    return False
        return True
           
print('测试')
bellmanford(graph,graph.nodes[1])

'''
总结:图的单元最短路径问题:
1.权都>=0:一定用特斯拉方法.因为他速度快
2.bellman方法为啥么一定要松弛|vertex|-1次呢?
因为每一个点无法用自己把自己松弛了.只能用其他人把自己松弛了.所以至多上面这么多次.才合理
但是如果有负权圈.那么还会继续下降.
原因就是start这个点的0会发生变化,负权图会把0给降低.这显然就不符合最短路径了.所以直接return False

'''
#下面测试优先队列里面修改后是否优先队列还能自动维护堆的性质.
class node2():
    def __init__(self,val):
     self.distance=val
    def __cmp__(self,other):
        return cmp(self.distance, other.distance)
    def __lt__(self,other):#operator <
        return self.distance < other.distance
    def __ge__(self,other):#oprator >=
        return self.distance >= other.distance
    def __gt__(self,other):#oprator >=
        return self.distance > other.distance
    def __le__(self,other):#oprator <=
        return self.distance <= other.distance



#下面举个例子来说明索引堆的妙用:和堆的区别:
pq = PQueue()
a=node2(1)
b=node2(2)
c=node2(3)
pq.put(a)
pq.put(b)
pq.put(c)
a.distance=999999999999
print(pq.get().distance)#结果显然不维护.所以上面我写的特斯拉算法是错误的,还是需要索引堆来实现.
class node1():
    def __init__(self,val):
     self.distance=val
pq = indexminheap(999)
a=node1(1)
b=node1(2)
c=node1(3)
pq.insert(1,a.distance)   #插入元素第一个1,表示index,第二个a.distance表示他的优先级系数.
pq.insert(2,b.distance)
pq.insert(3,c.distance)
pq.change(1,999)
print(pq.pop())#用索引堆显然就得到了2.就是我们要的效果.用索引来修改堆中任意元素并且能维护堆
#这就是索引堆的强大之处.可以随意修改堆中元素的属性,还能同时维护堆的结构


#下面写floyd算法:他是求图中所有的2点之间的距离.当然效率N^3,很慢.只返回距离,不记录中间路线
def floyd(graph):
    node_all=graph.nodes.values()
    output={}#key是(from,to) value是distance
    #初始化output.#也就是把边的信息给output
    #自己到自己距离是0
    for i in node_all:
        output[(i,i)]=0
    for i in graph.edges:
        output[(i.from1,i.to)]=i.weight

    for k in node_all:#k是中间点
        for i in node_all:
            for j in node_all:
                #python 括号内部的东西随便换行
                output[(i,j)]=min(output.setdefault((i,k),float('inf'))+
                                  output.setdefault((k,j),float('inf')),
                                  output.setdefault((i,j),float('inf')))
    index_output={}
    
    for i in output:
        
        index_output[(i[0].val,i[1].val)]=output[i]
    return output,index_output

print(floyd(graph)[1])
'''
graph = GraphGenerator([[1,2,10],[1,4,5],[2,3,1],[2,4,2],[3,5,4],[4,2,3],[4,3,9],[4,5,2],[5,1,7]
                        ,[5,3,6]])上一行代码返回这个图的两两点最短距离,注意写的是单向图,2到4距离2
                        4到2距离是3.对于双向图很容易只需要把边里面from,to互换后跟以前矩阵一起送
                        给graph即可.
'''
View Code

 

转载于:https://www.cnblogs.com/zhangbo2008/p/9176825.html

你可能感兴趣的:(图论题目模板,和并查集:以后的图论题目就靠他了)