286 阅读2分钟

图的逻辑结构和具体实现

一幅图由节点和边组成,逻辑结构如下:

image.png 我们用邻接表邻接矩阵表示图。

# 邻接表
graph = [[4,3,1],[3,2,4],[3],[4],[]]
# 邻接矩阵
matrix = [
    [False, True, False, True, True], 
    [False, False, True, True, True], 
    [False, False, False, True, False], 
    [False, False, False, False, True], 
    [False, False, False, False, False]]
// 邻接表
int[][] graph = {{4,3,1},{3,2,4},{3},{4},{}};
// 邻接矩阵
int[][] matrix = {
            {false,true,false,true,true},
            {false,false,true,true,true},
            {false,false,false,true,false},
            {false,false,false,false,true},
            {false,false,false,false,false}};

邻接表占用的空间少,邻接矩阵可以快速判断两个节点是否相邻。 其他图的模型如加权图、无向图等都是基于有向无权图衍生出来的。 有向加权图的邻接表,不仅仅存储某个节点x的所有邻居节点,还存储x到邻居的权重。邻接矩阵中matrix[x][y]不再是布尔值,而是一个int值,0表示没有连接,其他值表示权重。 无向图其实就是双向图。

图的遍历

图的遍历参考多叉树,多叉树的遍历框架如下:

def traverse(TreeNode root):
    if not root:
        return
    for child in root.children:
        traverse(child)
}

图和多叉树的最大区别就是,图可能包含环,从图的某一个节点开始遍历,有可能走了一圈又回到这个节点。 所以,如果图包含环,遍历框架就要一个visited数组进行辅助:

# 图遍历框架
graph = [[4,3,1],[3,2,4],[3],[4],[]]
visited = [False for i in range(5)]
def traverse(graph,s):
    if visited[s]:
        return
    visited[s] = True
    print('enter: ',s)
    for neighbor in graph[s]:
        traverse(graph,neighbor)
    visited[s] = False
    print('leave: ',s)
traverse(graph,0)

这个框架和回溯算法的框架类似,区别在于回溯算法的「做选择」和「撤销选择」在for循环里,而对visited数组的操作在for循环外。 在for循环里面和外面唯一的区别就是对根节点的处理,在for循环外面会记录所有节点的进入和离开信息,在for里面会忽略根节点的进入和离开信息。 回溯算法关注的不是节点,而是树枝,所以可以忽略根节点。 对于图的遍历,应该把visited的操作放到for循环外面,否则会漏掉起始点的遍历。

题目

797.所有可能的路径