图 一

1.图

图表示"多对多"的关系

  • 图:由顶点(vertex)和顶点与顶点间的连接–边(edge)构成。
  • 边:是一个顶点对,包括有向边、无向边(即双向边)、有向有权重、无向无权重
    • 有向边:只一个顶点指向另一个顶点的边(单行线)
    • 不考虑重边和自回路
  • 邻接点:有边直接相连的顶点
  • 出度:从一个顶点发出的边数
  • 入度:指向一个顶点的边数

2. 图的数据结构

图只是一个抽象的辅助解决问题的概念,没有必要一定用以下的邻接表、邻接矩阵表示。

2.1 邻接矩阵

顶点从0 到n-1编号。如果是无权重图,矩阵中值为1,代表行顶点到列顶点的边,值为0,代表没有边;如果是有权重图,矩阵中不为0的值代表权重大小,值为0,代表没有边。

  • 不允许自回路,对角线为0;
  • 如果是无向边,矩阵对称,会浪费空间
  • 数值表示为权重
  • 方便找邻接点(扫描此顶点在矩阵中的一行和一列)
  • 方便统计出度/入度(对于无向图,对应行非0元素;有向图:行非零数,出度,列非零数,入度)
  • 浪费空间,存稀疏图(点多变量少)有大量无效元素;对稠密图很合算
  • 浪费时间,统计稀疏图共有多少边
2.2 邻接表
  1. 顶点
class Node:
    def __init__(self,name):
        self.name = name
    def getName(self):
        return self.name
    def __str__(self):
        return self.name
  1. 边(一个顶点指向另一个顶点)
#无权重的边
class Edge:
    def __init__(self,src,dest):
        self.src = src
        self.dest = dest
    
    def getSource(self):
        return self.src
    
    def getDestination(self):
        return self.dest
    def __str__(self):
        return self.src.getName() + '->' + self.dest.getName()
#有权重的边
class WeightedEdge(Edge):
    def __init__(self,src,dest,weight=1.0):
        self.src = src
        self.dest = dest
        self.weight = weight
        
    def getWeight(self):
        return self.weight
    def __str__(self):
        return self.src.getName() + '->(' + str(self.weight)+')'+self.dest.getName()
    
  1. 图(图由顶点,和边构成)
#单向图,边用字典结构
class DiGraph:
    def __init__(self):
        self.nodes=[]
        self.edges = {}
    
    def addNode(self,node):
        if node in self.nodes:
            raise ValueError("Dupicate node!")
        else:
            self.nodes.append(node)
            self.edges[node] = []
    
    def addEdge(self,edge):
        src = edge.getSource()
        dest = edge.getDestination()
        if(src in self.nodes and dest in self.nodes):
            self.edges[src].append(dest)
        else:
            raise ValueError("node not in graph")
    
    def childrenOf(self,node):
        return self.edges[node]
    
    def hasNode(self,node):
        return node in self.nodes
    def __str__(self):
        result =''
        for src in self.nodes:
            for dest in self.edges[src]:
                result = result + src.getName() + '->' + dest.getName + '\n'
        return result[:-1]
#无向图,是单向图的子类,即双向图
class Graph(DiGraph):
    def addEdge(self,edge):
        super.addEdge(edge)
        newEdge = Edge(edge.getDestination(),edge.getSrc())
        super.addEdge(newEdge)
  • 在图中对每个顶点,存储一个连接顶点的列表,一定要够稀疏用邻接表表示才合算
  • 找任意一个顶点的所有邻接点
  • 节约稀疏图的空间
  • 对于无向图,方便计算任一顶点的“度”;对有向图,只能计算“出度”,需要构造“逆邻接表”计算“入度”

3.连通图的概念

  • 路径:两个顶点v、w,v到w的路径是一系列顶点的序列,序列中相邻顶点间都是邻接点。
  • 路径的长度:无权图,是路径中的边数;有权图,是所有边的权重和
  • 简单路径:路径中没有重复的顶点
  • 回路:起点==终点
  • 连通:如果顶点v到顶点w存在一条路径,那么v和w是连通的
  • 连通图:图中任意两个顶点均连通
  1. 对于无向图
  • 连通分量:当前无向图的极大连通子图
    • 极大顶点数:再加一个顶点就不连通了
    • 极大边数:包含子图中所有顶点相连的所有边
  1. 对于有向图
  • 强连通:有向图中顶点v和w之间存在双向路径(例如环),则v和w是强连通的
  • 强连通图:有向图中任意两顶点都强连通
  • 弱联通图:有向图中将单向变为无向后任意两顶点都连通
  • 强连通分量:有向图的极大强连通子图

4.图的遍历

如果图中的顶点都连通,没有单独的顶点

4.1 广度优先(Breadth first search, BFS)

从图的起点开始访问,依次访问起点的邻接点,再访问邻接点的邻接点。
但某些顶点间可能存在回路,我们如果一味的根据当前顶点的邻接点访问,有可能会出现循环,所以要检查一下当前顶点是否在路径中访问过。

from collections import deque
def BFS(graph, startNode):
    initPath = deque([startNode])
    pathDeque = deque([initPath])
    while len(pathDeque) >0:
        currPath = pathDeque.popleft()
        lastNode = currPath[-1]
        print(lastNode.name)
        # 在待检查的路径中添加最后一个顶点的邻接点路径
        for childNode in graph.childrenOf(lastNode):
            # 如果最后一个顶点的邻接点没有在当前路径中遍历过(避免循环遍历)
            if childNode not in currPath:
                # 新添路径
                newPath = currPath + deque([childNode])
                pathDeque.append(newPath)
    return None

如果用邻接表表示图,时间复杂度为O(N+E),N是顶点数,E是边数;用邻接矩阵表示,复杂度为O(N^2)

4.2 深度优先(depth first search, DFS)

如果当前顶点存在邻接点,访问第一个邻接点,再访问这个邻接点的第一个邻接点。如果不存在邻接点,回溯到父顶点,访问父顶点的定一个邻接点

def DFS(graph,start, path):
    path = path + [start]
    print(start)
    for node in graph.childrenOf(start):
        if node not in path:
         	DFS(graph,node,end,path,shortest)

非递归实现

def myDFS(graph,startNode):
    initPath = deque([startNode])
    pathDeque = deque([initPath])
    while len(pathDeque) > 0:
        currPath = pathDeque.popleft()
        lastNode = currPath[-1]
        # 为该节点的所有邻接点定义顺序
        priorPath = deque([])
        for childNode in graph.childrenOf(lastNode):
            #如果当前邻接点没有在当前路径中走过
            if childNode not in currPath:
                newPath = currPath + deque([childNode])
                priorPath.append(newPath)
        pathDeque =priorPath + pathDeque
    return bestPath
4.3不连通的图遍历
def ListVertext(graph ):
	path = []
    for node in graph.nodes:
        //如果有点没有被访问过,以此点为起始点进行遍历
        if node not in path:
        	DFS(graph, node, path)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值