代码随想录-图论

图论

一、理论基础

参见[图论理论基础]

(一)、基本概念

图论中的图是由一组顶点和一组边组成的,边连接顶点。顶点也称为[节点],代表实体,边代表顶点之间的关系。边可以是无向的,也可以是有向的。

  • 无向边表示顶点之间的关系是对称的,由无向边和定点组成的图为无向图
  • 有向边则表示顶点之间的关系是有方向的。由有向边和顶点组成的图为有向图

1、度

在无向图中,一个顶点的度是指,与该顶点相关联的边的数量。

在有向图中,顶点的度分为出度和入度。出度 是指从该顶点出发的边的数量,入度是指进入该顶点边的数量。

2、路径

从一个顶点到另一个顶点的边的序列,称为路径。路径的长度是指,路径中边的数量。

如果路径中没有重复的顶点,那就是简单路径。如果路径中的起点和终点相同,那么该路径称为回路或者环。

3、连通性

在图中,如果两个顶点之间存在路径,则说明这两个顶点是连通 的。如果一个图的任意两个顶点都是连通的,那么这个图就被称为连通图

在有向图中,对于任意的两个顶点 <math xmlns="http://www.w3.org/1998/Math/MathML"> U U </math>U和 <math xmlns="http://www.w3.org/1998/Math/MathML"> V V </math>V,若同时存在 <math xmlns="http://www.w3.org/1998/Math/MathML"> U → V U \rightarrow V </math>U→V和 <math xmlns="http://www.w3.org/1998/Math/MathML"> V → U V \rightarrow U </math>V→U的路径,则说明该有向图是强连通的。

若将该有向图是作为无向图之后,无向图是连通的,则该图是弱连通的。

4、连通分量

无向图中的极大连通子图,一个无向图可以有多个连通分量。整个图是连通的,当前仅当它只有一个连通分量。

5、强连通分量

有向图中的极大连通子图,一个有向图可以有多个强连通分量。

6、生成树

一个连通无向图的生成树是指该图的一个子图,它是一棵树,包含图中的所有顶点。生成树通常用于寻找最小生成树,即权重之和最小的生成树。

7、生成森林

一个无向图的生成森林是指,该图的每一个连通分量的生成树的集合。

(二)、图的存储

一般,图的存储方式为邻接表和邻接矩阵。

1、邻接表

邻接表是一种用数组和链表结合起来表示图的方式。数组中的每一个元素表示顶点,其指向一个链表,链表中存储的是与该顶点相连的顶点。

java 复制代码
import java.util.ArrayList;
import java.util.LinkedList;

public class Graph {
    private int vertices; // 顶点数量
    private ArrayList<LinkedList<Integer>> adjacencyList; // 邻接表

    // 构造函数
    public Graph(int vertices) {
        this.vertices = vertices;
        adjacencyList = new ArrayList<>(vertices);

        // 初始化邻接表
        for (int i = 0; i < vertices; i++) {
            adjacencyList.add(new LinkedList<>());
        }
    }

    // 添加一条边 u->v
    public void addEdge(int u, int v) {
        adjacencyList.get(u).add(v);
    }

    // 打印图的邻接表
    public void printGraph() {
        for (int i = 0; i < vertices; i++) {
            System.out.print("顶点 " + i + " 的邻接表:");
            for (int neighbor : adjacencyList.get(i)) {
                System.out.print(neighbor + " ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        Graph graph = new Graph(5); // 创建一个有5个顶点的图
        graph.addEdge(0, 1);
        graph.addEdge(0, 4);
        graph.addEdge(1, 2);
        graph.addEdge(1, 3);
        graph.addEdge(1, 4);
        graph.addEdge(2, 3);
        graph.addEdge(3, 4);

        graph.printGraph();
    }
}

2、邻接矩阵

邻接矩阵是一个二维数组,用于表示顶点之间的连接关系里如果顶点ij之间有边,则matrix[i][j]之间的值为1,否则为0

java 复制代码
public class Graph {
    private int vertices; // 顶点数量
    private int[][] adjacencyMatrix; // 邻接矩阵

    // 构造函数
    public Graph(int vertices) {
        this.vertices = vertices;
        adjacencyMatrix = new int[vertices][vertices];
    }

    // 添加一条边 u->v
    public void addEdge(int u, int v) {
        adjacencyMatrix[u][v] = 1;
        // 如果是无向图,还需要设置 adjacencyMatrix[v][u] = 1
    }

    // 打印图的邻接矩阵
    public void printGraph() {
        System.out.println("顶点之间的邻接矩阵:");
        for (int i = 0; i < vertices; i++) {
            for (int j = 0; j < vertices; j++) {
                System.out.print(adjacencyMatrix[i][j] + " ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        Graph graph = new Graph(5); // 创建一个有5个顶点的图
        graph.addEdge(0, 1);
        graph.addEdge(0, 4);
        graph.addEdge(1, 2);
        graph.addEdge(1, 3);
        graph.addEdge(1, 4);
        graph.addEdge(2, 3);
        graph.addEdge(3, 4);

        graph.printGraph();
    }
}

邻接表和邻接矩阵的比较:

邻接矩阵 邻接表
空间效率 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( V 2 ) O(V^2) </math>O(V2) <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( V + E ) O(V + E) </math>O(V+E)
增删顶点 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( 1 ) O(1) </math>O(1),需考虑数组扩容 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( 1 ) O(1) </math>O(1)
判断边 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( 1 ) O(1) </math>O(1) <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( E / V ) O(E / V) </math>O(E/V)
适用场景 密集图,结构稳定 稀疏图,图结构动态变化比较大

(三)、图的遍历

图的遍历主要有两种,广度优先遍历(Board first search,BFS)和深度优先遍历(Deep first search, DFS)。

1、深度优先遍历

深度优先遍历就是,选择一个尚未被访问过的顶点,递归进行深度优先搜索,直到没有未访问过的邻接顶点为止。然后回溯到上一个顶点,继续访问其他未访问过的顶点。

DFS框架:

  1. 定义一个布尔数组boolean[] visited,记录某一个顶点是否被访问过
  2. 从某个顶点 <math xmlns="http://www.w3.org/1998/Math/MathML"> v i v_i </math>vi开始,标记该顶点已经被访问
  3. 遍历 <math xmlns="http://www.w3.org/1998/Math/MathML"> v i v_i </math>vi的所有邻接顶点 <math xmlns="http://www.w3.org/1998/Math/MathML"> w j w_j </math>wj,对于每个未访问 的 <math xmlns="http://www.w3.org/1998/Math/MathML"> w j w_j </math>wj,递归调用DFS函数
java 复制代码
public void DFS(int startVertex) {
    boolean[] visited = new boolean[vertices];
    DFSUtil(startVertex, visited);
}

private void DFSUtil(int v, boolean[] visited) {
    visited[v] = true;
    System.out.print(v + " ");

    for (int neighbor : adjacencyList.get(v)) {
        if (!visited[neighbor]) {
            DFSUtil(neighbor, visited);
        }
    }
}

2、广度优先遍历

从某个节点出发,先访问该顶点,然后一次访问其所有未访问过的邻接顶点,再按照这些邻接顶点的顺序一次访问其未访问过的邻接顶点,直到所有的节点都被访问过为止。

BFS框架

  1. 定义一个布尔数组boolean[] visited,记录每一个及诶的那是否被访问过
  2. 使用一个队列来辅助遍历,将起始顶点v入队,并标记为已经访问
  3. 当队列不为空的时候,取出队首元素,访问该节点,并将其所有未访问过的顶点入队,并标记为已经访问
java 复制代码
public void BFS(int startVertex) {
        boolean[] visited = new boolean[vertices];
        Queue<Integer> queue = new LinkedList<>();
        visited[startVertex] = true;
        queue.add(startVertex);

        while (!queue.isEmpty()) {
            int v = queue.poll();
            System.out.print(v + " ");

            for (int neighbor : adjacencyList.get(v)) {
                if (!visited[neighbor]) {
                    visited[neighbor] = true;
                    queue.add(neighbor);
                }
            }
        }
相关推荐
uhakadotcom1 分钟前
Pandas入门:数据处理和分析的强大工具
后端·面试·github
Asthenia04124 分钟前
Json里面想传图片(验证码图)-Base64编码来助你!
后端
服务端技术栈38 分钟前
MySQL 索引:数据库查询的“加速器”
后端
Asthenia04121 小时前
Redis与MySQL协同:旁路缓存机制
后端
hamburgerDaddy11 小时前
golang 从零单排 (一) 安装环境
开发语言·后端·golang
Asthenia04121 小时前
线程的生命周期状态你烂熟于心,那线程池呢?Running/ShutDown/Stop/Tidying/Terminated
后端
小画家~1 小时前
第本章:go 切片
开发语言·后端·golang
我是大咖1 小时前
c语言笔记 一维数组与二维数组
c语言·笔记·算法
誓约酱1 小时前
(每日一题) 力扣 283 移动零
linux·c语言·数据结构·c++·算法·leetcode
浪遏1 小时前
面试官:字符串反转有多少种实现方式 ?| 一道题目检测你的基础
前端·面试