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


    '''
    并查集:
    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.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=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.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=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.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=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
  • 相关阅读:
    剑指offer4:重建二叉树(后序遍历)
    剑指offer3:从尾到头打印链表每个节点的值
    剑指offer2:C++实现的替换空格(字符中的空格替换为“%20”)
    tp5系统变量输出(可以用来传递搜索的参数)
    Ajax实现文件上传的临时垃圾文件回收策略
    php获取当天的开始时间和结束时间
    Think PHP递归获取所有的子分类的ID (删除当前及子分类)
    tp查找某字段,排除某字段,不用一次写那么多
    git-查看历史版本及回滚版本
    dedecms目录结构,非常全
  • 原文地址:https://www.cnblogs.com/zhangbo2008/p/9176825.html
Copyright © 2020-2023  润新知