2022.3.22 图论基础

本文介绍了图论的基础知识,包括图的实现方法(邻接表和邻接矩阵),强调邻接表在空间效率上的优势,以及邻接矩阵在判断节点相邻时的高效。此外,还详细讨论了图的遍历,考虑到可能存在环的情况,引入visited和onPath数组避免死循环。并通过一个具体的例题797. 所有可能的路径,展示了如何在有向无环图中找到所有路径。最后,提到了二维数组的不同声明方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


文章参考自

前言:图可以玩出更多的算法,解决更复杂的问题,但本质上图可以认为是多叉树的延伸。面试笔试很少出现图相关的问题,就算有,大多也是简单的遍历问题,基本上可以完全照搬多叉树的遍历。

一、图的具体实现

在这里插入图片描述
图的实现方法①:逻辑上的实现

/* 图节点的逻辑结构 */
class Vertex {
    int id;
    Vertex[] neighbors;
}

类似多叉树节点

/* 基本的 N 叉树节点 */
class TreeNode {
    int val;
    TreeNode[] children;
}

图本质上就是个高级点的多叉树,适用于树的 DFS/BFS 遍历算法,全部适用于图。

图的实现方法②:邻接表
实际使用中更常用的是邻接表
在这里插入图片描述
把每个节点 x 的邻居都存到一个列表里,然后把 x 和这个列表关联起来,这样就可以通过一个节点 x 找到它的所有相邻节点。

// 邻接表
// graph[x] 存储 x 的所有邻居节点
List<Integer>[] graph;

图的实现方法③:邻接矩阵
在这里插入图片描述
邻接矩阵则是一个二维布尔数组,我们权且称为 matrix,如果节点 x 和 y 是相连的,那么就把 matrix[x][y] 设为 true(上图中绿色的方格代表 true)。如果想找节点 x 的邻居,去扫一圈 matrix[x][…] 就行了。

// 邻接矩阵
// matrix[x][y] 记录 x 是否有一条指向 y 的边
boolean[][] matrix;

邻接表 VS 邻接矩阵:

邻接表,好处是占用的空间少。
邻接矩阵里面空着那么多位置,肯定需要更多的存储空间。

但是,邻接表无法快速判断两个节点是否相邻。
比如说我想判断节点 1 是否和节点 3 相邻,我要去邻接表里 1 对应的邻居列表里查找 3 是否存在。但对于邻接矩阵就简单了,只要看看 matrix[1][3] 就知道了,效率高。

所以说,使用哪一种方式实现图,要看具体情况。

二、图的遍历

遍历过程:
在这里插入图片描述
(我们需要引入两个数组,visited 数组用于处理图中的环,防止递归重复遍历同一个节点进入死循环;onPath 数组用于存储临时路径。
在 visited 中被标记为 true 的节点用灰色表示,在 onPath 中被标记为 true 的节点用绿色表示)

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

/* 多叉树遍历框架 */
void traverse(TreeNode root) {
    if (root == null) return;

    for (TreeNode child : root.children) {
        traverse(child);
    }
}

图和多叉树最大的区别是,图是可能包含环的,你从图的某一个节点开始遍历,有可能走了一圈又回到这个节点。

所以,如果图包含环,遍历框架就要一个 visited 数组进行辅助:

// 记录被遍历过的节点
boolean[] visited;
// 记录从起点到当前节点的路径
boolean[] onPath;

/* 图遍历框架 */
void traverse(Graph graph, int s) {
    if (visited[s]) return;
    // 经过节点 s,标记为已遍历
    visited[s] = true;
    // 做选择:标记节点 s 在路径上
    onPath[s] = true;
    for (int neighbor : graph.neighbors(s)) {
        traverse(graph, neighbor);
    }
    // 撤销选择:节点 s 离开路径
    onPath[s] = false;
}

三、例题

1.第八部分:图 – 797. 所有可能的路径

给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序)

graph[i] 是一个从节点 i 可以访问的所有节点的列表(即从节点 i 到节点 graph[i][j]存在一条有向边)。

示例 1:

输入:graph = [[1,2],[3],[3],[]]
输出:[[0,1,3],[0,2,3]]
解释:有两条路径 0 -> 1 -> 3 和 0 -> 2 -> 3

示例 2:

输入:graph = [[4,3,1],[3,2,4],[3],[4],[]]
输出:[[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]]

答案代码:
graph 其实就是用「邻接表」表示的一幅图,graph[i] 存储节点 i 的所有邻居节点。
在这里插入图片描述
本题的遍历思想本质上是以 0 为起点遍历图,同时记录遍历过的路径,当遍历到终点时将路径记录下来即可。

package Graph;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * @author: LYZ
 * @date: 2022/3/22 12:14
 * @description: 797. 所有可能的路径
 */
public class AllPathsSourceTarget {
    public static void main(String[] args) {
        int[][] graph = {{1,2},{3},{3},{}};
        AllPathsSourceTarget all = new AllPathsSourceTarget();
        List<List<Integer>> ans = all.allPathsSourceTarget(graph);
        for (List<Integer> list : ans) {
            System.out.println(list);
        }
    }

    // 记录所有路径
    List<List<Integer>> res = new LinkedList<>();

    public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
        // 维护递归过程中经过的路径
        LinkedList<Integer> path = new LinkedList<>();
        traverse(graph, 0, path);
        return res;
    }

    /* 图的遍历框架 */
    void traverse(int[][] graph, int s, LinkedList<Integer> path) {
        // 添加节点 s 到路径
        path.addLast(s);

        int n = graph.length;
        if (s == n - 1) {
            // 到达终点
            res.add(new LinkedList<>(path));
            path.removeLast();
            return;
        }

        // 递归每个相邻节点
        for (int v : graph[s]) {
            traverse(graph, v, path);
        }

        // 从路径移出节点 s
        path.removeLast();
    }
}

四、二维数组的声明:

参考自

四种方式:
在这里插入图片描述
1.

类型 数组名[][ ]={{初始值1},{初始值2},{初始值3}};

在这里插入图片描述
2.

类型 数组名[][ ]=new 类型[长度][长度];

在这里插入图片描述
3.

类型 数组名[][ ]=new 类型[长度][];

在这里插入图片描述
4.

类型 数组名[][ ]=new类型[][]{初始值1},{初始值2},{初始值3}};

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值