图是什么?
图由节点和边构成。
图和树很像,但树所有节点的子节点都是唯一的,图的节点的子节点(叫相邻节点更合适)是有可能相同的。
如上示例,0 的相邻节点是 4,3 的相邻节点也是 4,树是不可能这样的,所以树也是特殊的图。
图如何存储
两种方法:邻接表和邻接矩阵,如上示例,存储如下:
邻接表解释:
节点 0 可通向节点 4,节点 3,节点 1。
节点 1 可通向节点 3,节点 2,节点 4。
以此类推,索引代表当前的节点,索引值代表当前节点可通向的节点。
邻接矩阵也是类似,它用了一个 N * N 的二维数组,数组值只能是 0 或 1,0 的话代表不通,1 代表通,如:
代表 2 号节点和 3 号节点是联通的,和 0、1、2、4 是不通的。
邻接表 vs 邻接矩阵
邻接表:耗费的存储空间更少,更紧凑。
邻接矩阵:能更快速判断两个节点是否联通,只需要 O(1) 的时间。
实际情况要看场景使用,但邻接表使用的更多些。
度(degree)
这是图特有的概念,就是每个节点相连的边的条数,又因为有向图有方向,那么「度」被细分为入度(indegree)和出度(outdegree)。
还是此图为例,节点 3 的入度是 3,因为 0、1、2 都指向 3,出度是 1,因为 3 指向 4。
以上我们都是以有向无权图(边有方向但是没有权重的图)为例,那有向加权图、无向图怎么存储呢?
有向加权图
加权指边上有权重了,比如:0 到 1 权重需要 10,0 到 3 权重 20,0 到 4 权重 30,那么邻接表如下:
0: [1, 10], [3, 20], [4, 30]
其余节点同理
原来只存了边指向的节点,现在还存了节点上的权重。
无向图
这个更简单,如节点 0 到节点 4 是联通的,而且是无向图,你可以理解为:节点 0 的边指向节点 4,并且节点 4 的边也指向节点 0,2 个节点互相指,那就可以理解为无向的了。
实战题目
给你一个有方向但是没有环的图结构,求从节点 0 到节点 n-1 的所有路径,如示例就有 2 种路径:
从 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)
})
}
学算法的关键在于多刷题,你可以先选择和图相关的算法,之后按照先简单,先通过率高的题目顺序来刷题。
本文为学习总结,原文地址