TypeScript 图算法:基础知识与遍历

58 阅读3分钟

微信截图_20230310234034.png

图是什么?

图由节点和边构成。

image.png

图和树很像,但树所有节点的子节点都是唯一的,图的节点的子节点(叫相邻节点更合适)是有可能相同的。

如上示例,0 的相邻节点是 4,3 的相邻节点也是 4,树是不可能这样的,所以树也是特殊的图。

图如何存储

两种方法:邻接表和邻接矩阵,如上示例,存储如下:

image.png

邻接表解释:

image.png

节点 0 可通向节点 4,节点 3,节点 1。

image.png

节点 1 可通向节点 3,节点 2,节点 4。

以此类推,索引代表当前的节点,索引值代表当前节点可通向的节点。

邻接矩阵也是类似,它用了一个 N * N 的二维数组,数组值只能是 0 或 1,0 的话代表不通,1 代表通,如:

image.png

代表 2 号节点和 3 号节点是联通的,和 0、1、2、4 是不通的。

邻接表 vs 邻接矩阵

邻接表:耗费的存储空间更少,更紧凑。

邻接矩阵:能更快速判断两个节点是否联通,只需要 O(1) 的时间。

实际情况要看场景使用,但邻接表使用的更多些。

度(degree)

这是图特有的概念,就是每个节点相连的边的条数,又因为有向图有方向,那么「度」被细分为入度(indegree)和出度(outdegree)。

image.png

还是此图为例,节点 3 的入度是 3,因为 0、1、2 都指向 3,出度是 1,因为 3 指向 4。

以上我们都是以有向无权图(边有方向但是没有权重的图)为例,那有向加权图、无向图怎么存储呢?

有向加权图

加权指边上有权重了,比如:0 到 1 权重需要 10,0 到 3 权重 20,0 到 4 权重 30,那么邻接表如下:

image.png

0: [1, 10], [3, 20], [4, 30]
其余节点同理

原来只存了边指向的节点,现在还存了节点上的权重。

无向图

这个更简单,如节点 0 到节点 4 是联通的,而且是无向图,你可以理解为:节点 0 的边指向节点 4,并且节点 4 的边也指向节点 0,2 个节点互相指,那就可以理解为无向的了。

image.png

实战题目

题目链接

image.png

给你一个有方向但是没有环的图结构,求从节点 0 到节点 n-1 的所有路径,如示例就有 2 种路径:

image.png

从 0 到 3,红色一种,蓝色一种。这里解决的问题就是如何遍历整个图,首先从 0 开始走,并记录走过的路径,之后一直往相邻节点走,并且重复此步骤,如果遇到了节点是 n-1 的情况,那么就不能继续走了,因为找到答案了,记录答案即可。

如果你熟悉递归和树结构,这里和用递归去遍历树的方法一样:

function allPathsSourceTarget(graph: number[][]): number[][] {
    let n = graph.length
    let res: number[][]=[]
    tarverse(n,graph,0,[0],res)
    return res
};

function tarverse(n:number,graph:number[][],curNode: number,path:number[],res:number[][]){
    if(curNode===n-1){
        res.push(path)
    }
    const neighbor = graph[curNode]
    neighbor.forEach(i=>{
        tarverse(n,graph,i,[...path,i],res)
    })
}

学算法的关键在于多刷题,你可以先选择和图相关的算法,之后按照先简单,先通过率高的题目顺序来刷题。

本文为学习总结,原文地址