• 补学图论算法:算法竞赛入门经典(第二版)第十一章:


    补学图论算法:算法竞赛入门经典(第二版)第十一章:

    倒排索引还没有实现!

    下面是左神的图论算法,并查集笔记.和一个美团题目.

    '''
    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=[]
    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
  • 相关阅读:
    LinkedList的使用方法
    规范HTML页面
    HTML总结(一)
    HTML标签学习总结
    java---线程池的使用
    java对excel表格的操作
    java对cookie及Session的操作
    硬盘分区工具gparted使用
    镜像, 转置, 锐化, 灰度,旋转
    ffmpeg解码
  • 原文地址:https://www.cnblogs.com/zhangbo2008/p/9166503.html
Copyright © 2020-2023  润新知