【韩顺平-数据结构】图(学习笔记)

一、图的概述

1、图的概念

图是一种数据结构,其中结点可以具有零个或多个相邻元素。两个结点之间的连接称为边。(结点也可以称为顶点)

2、图的基本概念

  1. 顶点(vertex)
  2. 边(edge)
  3. 路径
  4. 无向图:顶点之间的连接没有方向
  5. 有向图:顶点之间的连接有方向
  6. 带权图(网):边带权值的图

3、图的实现方式

图的表示方式有两种:二维数组表示(邻接矩阵);链表表示(邻接表)

1)邻接矩阵

邻接矩阵是表示图形中顶点之间相邻关系的矩阵(二维数组),对于n个顶点的图而言,矩阵是的row 和col 表示的是 1…n个点。

2)邻接表

邻接矩阵需要为每个顶点都分配n 个边的空间,其实有很多边都是不存在,会造成空间的一定损失。

邻接表的实现只关心存在的边,不关心不存在的边。因此没有空间浪费,邻接表由数组+链表组成。

二、深度优先搜索(dfs)

深度优先遍历,从初始访问结点出发,初始访问结点可能有多个邻接结点,深度优先遍历的策路就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点。

每次都在访问完当前结点后首先访问当前结点的第一个邻接结点。

这样的访问策略是优先往纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问。

深度优先搜索是一个递归的过程

三、广度优先搜索(bfs)

图的广度优先搜索(Broad First Search),类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的结点的顺序,以便按这个顺序来访问这些结点的邻接结点

void bfs(起始点) {
    将起始点放入队列中;
    标记起点访问;
    while(如果队列不为空){
        访问队首元素x;
        删除队首元素;
        for(x的相邻点) {
            if(没被标记) {
                加入队尾并标记; 
            } 
        } 
    } 
    队列为空,广搜结束; 
}

四、图的代码实现

package work.rexhao.graph;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 图的基础及dfs、bfs遍历
 */
public class Graph {
    int[][] edges;      // 邻接矩阵
    int n;              // 节点个数
    List<String> vertex;// 顶点名称
    boolean[] flag;     // 记录是否被访问过

    public static void main(String[] args) {
        Graph graph = new Graph(5);
        String[] strs = {"a", "b", "c", "d", "e"};
        graph.addVertex(strs);

        graph.addEdges(0, 1, 1);  // a -- b
        graph.addEdges(0, 2, 1);  // a -- c
        graph.addEdges(2, 3, 1);  // c -- d
        graph.addEdges(0, 4, 1);  // a -- e

        graph.showGraph();
        System.out.println("-----------");

        graph.dfs();
        graph.bfs();
    }

    /**
     * 广度优先遍历
     */
    private void bfs() {
        flag = new boolean[n];
        System.out.print("bfs:");
        ArrayQueue queue = new ArrayQueue(n);

        queue.addQueue(0);
        while (!queue.isEmpty()) {
            // 出队列
            int i = queue.getQueue();
            System.out.print(getValueByIndex(i));
            // 标记
            flag[i] = true;
            // 把该节点连接的节点加入队列
            for (int j = 0; j < n; j++) {
                if (!flag[j] && edges[i][j] != 0) {
                    queue.addQueue(j);
                }
            }
        }

        System.out.println();
    }


    /**
     * 深度优先遍历
     */
    public void dfs() {
        flag = new boolean[n];
        System.out.print("dfs:");
        for (int i = 0; i < n; i++) {
            dfs(i);
        }
        System.out.println();
    }

    public void dfs(int i) {
        // 标记过 -> return
        if (flag[i]) {
            return;
        }
        // 没被标记 -> 输出该节点 -> 标记
        System.out.print(getValueByIndex(i));
        flag[i] = true;
        // 找到该节点的后续节点
        for (int j = 0; j < n; j++) {
            if (edges[i][j] != 0) {
                dfs(j);
            }
        }
    }

    /**
     * 构造器
     *
     * @param n 节点个数
     */
    Graph(int n) {
        this.n = n;
        edges = new int[n][n];
        vertex = new ArrayList<>(n);
    }

    /**
     * 添加节点名称
     *
     * @param strs 节点名称数组
     */
    public void addVertex(String[] strs) {
        vertex.addAll(Arrays.asList(strs));
    }

    /**
     * 添加边
     *
     * @param v1     节点1
     * @param v2     节点2
     * @param weight 权重
     */
    public void addEdges(int v1, int v2, int weight) {
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
    }

    /**
     * 打印邻接矩阵
     */
    public void showGraph() {
        for (int[] edge : edges) {
            for (int i : edge) {
                System.out.print(i + " ");
            }
            System.out.println();
        }
    }


    /**
     * 根据下标获取节点数据
     *
     * @param i 节点下标
     * @return 节点数据
     */
    public String getValueByIndex(int i) {
        return vertex.get(i);
    }
}

class ArrayQueue {
    private int maxSize; // 数组最大容量
    private int front; // 队列头
    private int rear; // 队列尾
    private int[] arr; // 队列的数据

    /**
     * 创建队列的构造器
     */
    public ArrayQueue(int arrMaxSize) {
        maxSize = arrMaxSize;
        arr = new int[arrMaxSize];
        front = -1;// 指向队列头的前一个位置
        rear = -1;// 指向队列尾
    }

    /**
     * 判断队列是否满
     */
    public boolean isFull() {
        return rear == maxSize - 1;
    }

    /**
     * 判断队列是否为空
     */
    public boolean isEmpty() {
        return rear == front;
    }

    /**
     * 添加队列数据
     */
    public void addQueue(int n) {
        if (isFull()) {
            System.out.println("队列满,添加失败!");
            return;
        }
        arr[++rear] = n;
    }

    /**
     * 出队列
     */
    public int getQueue() {
        if (isEmpty()) {
            // 抛出异常 -- 不需要写return
            throw new RuntimeException("队列空");
        }
        return arr[++front];
    }

    /**
     * 遍历
     */
    public void showQueue() {
        if (isEmpty()) {
            System.out.println("队列空!");
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.printf("arr[%d] = %d\n", i, arr[i]);
        }
    }

    /**
     * 显示头数据(不取出)
     */
    public int headQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列空");
        }
        return arr[front + 1];
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
B站上的韩顺平老师的《Linux学习笔记》系列课程非常值得推荐。通过这个课程,我学到了很多关于Linux操作系统的知识和技能。 首先,韩老师在课程中详细介绍了Linux的基本概念和特点。我清楚地了解到Linux是一个开源的操作系统,具有稳定性、安全性和可定制性强的特点。这让我对Linux有了更深入的理解,也更有信心去学习和使用它。 其次,韩老师从基础开始,逐步讲解了Linux的安装和配置。他用简单明了的语言和实际操作的示范,帮助我了解了如何在虚拟机上安装Linux系统,并设置网络、用户账户、文件系统等。这为我后续的学习和实践打下了坚实的基础。 此外,韩老师还讲解了Linux的常用命令和工具。他详细介绍了常用的文件和目录操作命令,比如cd、ls、mkdir、cp等。同时,他还讲解了grep、sed、awk等强大的文本处理工具的使用方法。这些内容帮助我更加高效地进行文件管理和数据处理。 最后,韩老师还介绍了Linux的网络管理和安全防护。他讲解了如何配置网络连接、使用ssh远程登录以及设置防火墙等内容。这些知识对我了解网络和保护系统安全非常有帮助。 总的来说,韩顺平老师的《Linux学习笔记》课程非常实用,对于初学者来说是入门学习Linux的好选择。他通过深入浅出的讲解和丰富的实操示范,让我可以轻松地学习到Linux的基本知识和操作技巧。我相信通过学习这个课程,我会在Linux领域有更进一步的发展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wmh1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值