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

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

倒排索引还没有实现!

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

'''
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]。 3 和 4 之间围成的面积为 0, 因为两个柱子是相邻的, 中间没有距离。 3 和
2 之间围成的面积为 2, 因为两个柱子的距离为 1, 且 2 是最短的柱子, 所以面积=1*2。3 和 5 之间围成的面积为 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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

15122306087

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值