DAY48

114 阅读3分钟

第十一章:图论part01

图论理论基础

大家可以在看图论理论基础的时候,很多内容 看不懂,例如也不知道 看完之后 还是不知道 邻接矩阵,邻接表怎么用, 别着急。

理论基础大家先对各个概念有个印象就好,后面在刷题的过程中,每个知识点都会得到巩固。

www.programmercarl.com/kamacoder/%… 在 JavaScript 中,我们可以用两种方式来构建图的数据结构:

  1. 邻接表:使用数组或对象来表示节点的连接,适合稀疏图。
  2. 邻接矩阵:用二维数组来表示节点之间的边权重,适合稠密图。

邻接表构建

邻接表适合用于 稀疏图(即节点多但边少的图)。每个节点拥有一个数组,数组中的每个元素表示该节点连接的其他节点,以及对应的边权重。

/**
 * 构建邻接表
 * @param {number} n - 节点数量
 * @param {number[][]} edges - 每条边的起点、终点、权重
 * @returns {Array} - 邻接表
 */
function buildAdjList(n, edges) {
    const adjList = Array.from({ length: n }, () => []);

    edges.forEach(([from, to, weight]) => {
        adjList[from].push([to, weight]);
        adjList[to].push([from, weight]);  // 无向图
    });

    return adjList;
}

// 示例使用
const n = 5;
const edges = [
    [0, 1, 4],
    [0, 2, 3],
    [1, 2, 1],
    [1, 3, 2],
    [2, 4, 5]
];
const adjList = buildAdjList(n, edges);
console.log(adjList);

邻接矩阵构建

邻接矩阵适合用于 稠密图(即边的数量接近节点数量的平方)。矩阵中 matrix[i][j] 的值表示节点 ij 的边权重。

/**
 * 构建邻接矩阵
 * @param {number} n - 节点数量
 * @param {number[][]} edges - 每条边的起点、终点、权重
 * @returns {Array} - 邻接矩阵
 */
function buildAdjMatrix(n, edges) {
    const adjMatrix = Array.from({ length: n }, () => Array(n).fill(Infinity));

    edges.forEach(([from, to, weight]) => {
        adjMatrix[from][to] = weight;
        adjMatrix[to][from] = weight;  // 无向图
    });

    return adjMatrix;
}

// 示例使用
const adjMatrix = buildAdjMatrix(n, edges);
console.log(adjMatrix);

两种方法的适用场景

  • 邻接表:更适合稀疏图,占用空间较少,且能够快速查找特定节点的邻居。
  • 邻接矩阵:适合稠密图,可以快速判断两个节点之间是否存在边,但空间复杂度较高。

深搜理论基础

了解一下深搜的原理和过程

www.programmercarl.com/kamacoder/%…

98. 所有可达路径

www.programmercarl.com/kamacoder/0… 是的,graph[i] 表示的是从节点 i 可以直接访问的所有节点。也就是说,graph[i] 是一个数组,包含了所有与节点 i 直接相连的节点列表。如果从 i 节点到 graph[i][j] 节点存在一条有向边,那么 graph[i][j] 就在这个数组中。

例子:

假设有这样一个图:

0 → 1 → 3
↓   
2 → 3   

其对应的邻接列表表示为:

let graph = [
    [1, 2],  // 节点0可以到达节点1和节点2
    [3],     // 节点1可以到达节点3
    [3],     // 节点2可以到达节点3
    []       // 节点3没有指向任何节点
];

在这种表示方式中,graph[i] 列表中的每个元素 graph[i][j] 表示存在一条从节点 i 到节点 graph[i][j] 的有向边。

解释:

  • 对于节点 0graph[0] = [1, 2] 表示从节点 0 有两条边分别指向节点 1 和节点 2
  • 对于节点 1graph[1] = [3] 表示从节点 1 有一条边指向节点 3
  • 对于节点 2graph[2] = [3] 表示从节点 2 有一条边指向节点 3
  • 节点 3 没有指向任何其他节点,因此 graph[3] = []

用这种方式进行 DFS:

根据这个表示法,我们可以通过 DFS 递归遍历所有从起点 0 到终点 n-1 的路径:

/**
 * @param {number[][]} graph
 * @return {number[][]}
 */
var allPathsSourceTarget = function(graph) {
    let res = [];
    let path = [0];  // 起始路径

    function dfs(cur, n) {
        if (cur === n) {
            res.push([...path]);  // 到达目标节点,保存路径
            return;
        }

        // 遍历当前节点的所有相邻节点
        for (let next of graph[cur]) {
            path.push(next);  // 进入下一个节点
            dfs(next, n);     // 深度优先搜索
            path.pop();       // 回溯
        }
    }

    dfs(0, graph.length - 1);  // 从节点0开始搜索
    return res;
};

运行:

let graph = [[1,2], [3], [3], []];
console.log(allPathsSourceTarget(graph));  
// 输出: [[0,1,3], [0,2,3]]

这个方法有效地找到所有从起点到终点的路径。

广搜理论基础

www.programmercarl.com/kamacoder/%…