代码随想录-图论

图论

一、理论基础

参见**图论理论基础**

(一)、基本概念

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

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

1、度

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

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

2、路径

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

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

3、连通性

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

在有向图中,对于任意的两个顶点 U U U和 V V V,若同时存在 U → V U \rightarrow V U→V和 V → U V \rightarrow U 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();
    }
}

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

邻接矩阵 邻接表
空间效率 O ( V 2 ) O(V^2) O(V2) O ( V + E ) O(V + E) O(V+E)
增删顶点 O ( 1 ) O(1) O(1),需考虑数组扩容 O ( 1 ) O(1) O(1)
判断边 O ( 1 ) O(1) O(1) O ( E / V ) O(E / V) O(E/V)
适用场景 密集图,结构稳定 稀疏图,图结构动态变化比较大

(三)、图的遍历

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

1、深度优先遍历

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

DFS框架:

  1. 定义一个布尔数组boolean[] visited,记录某一个顶点是否被访问过
  2. 从某个顶点 v i v_i vi开始,标记该顶点已经被访问
  3. 遍历 v i v_i vi的所有邻接顶点 w j w_j wj,对于每个未访问 w j w_j 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);
                }
            }
        }
相关推荐
青 春 记 忆9 小时前
LeetCode 21. 合并两个有序链表|Python 解法详解
python·算法·leetcode·链表
程序员爱钓鱼11 小时前
Rust Copy详解:隐式复制与轻量数据类型
前端·后端·rust
Sagittarius_A*11 小时前
后量子密码|通识认知 03|量子威胁辟谣:Grover 算法对对称密码的影响与误区
算法·信息安全·密码学·量子计算·pqc·后量子密码
仙人球部落 揞殺12 小时前
细说ASP.NET的各种异步操作
后端·asp.net
QAQo7T13 小时前
Python组蓝桥杯备赛超详细知识点总结笔记_排序算法篇
笔记·python·算法·蓝桥杯·排序算法
千里之行,始于足下sanhai13 小时前
P8686 [蓝桥杯 2019 省 A] 修改数组 - 数字魔法大冒险 题解
c++·算法·动态规划
小星星闪亮登场13 小时前
Codeforces Round 1115 ( Div. 2)
数据结构·c++·算法·贪心算法·排序算法·codeforces
计算机小白一个13 小时前
蓝桥杯 Java B 组之哈希表应用(两数之和、重复元素判断)
java·数据结构·算法·蓝桥杯
Loge编程生活13 小时前
打卡信奥刷题(3449)用C++实现信奥题 P10429 [蓝桥杯 2024 省 B] 拔河
开发语言·数据结构·c++·算法·青少年编程
卷无止境13 小时前
当独立开发者也能造出3A画质的游戏:Godot引擎深度解析
后端·python·godot