每天进步一点点【图的深度优先遍历(DFS)】

2023-11-16

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

图分为三种:无向图、有向图、带权图

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

邻接矩阵

邻接矩阵是表示图形中顶点之间相邻关系的矩阵,对于n个顶点的图而言,矩阵的行和列表示的是1……n个点。邻接矩阵需要为每个顶点都分配n个边的空间,其实有很多边都是不存在的,会造成空间的一定损失。

d0195c7dbd8e7e9afd9db11746148f51.png

邻接表

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

6397dce6ca85ad39fda43f78ce2b0ff1.png

说明:

  1. 标号为0的节点的相关联的节点为1,2,3,4

  2. 标号为1的节点为0,4

  3. 标号为2的节点的相关联的节点为0,4,5

  4. ……

代码实现图结构
497de495b075b18c15f759877d244f6a.png

/**
 * 邻接矩阵表示图
 * @author sixiaojie
 * @date 2022-01-07-19:05
 */
public class Graph {
    /** 存储顶点集合 */
    private ArrayList<String> vertexList;

    /** 存储图对应的邻接矩阵 */
    private int[][] edges;

    /** 表示边的数目 */
    private int numOfEdges;

    public static void main(String[] args) {
        // 顶点个数
        int n = 5;
        String[] vertexs = {"A","B","C","D","E"};
        Graph graph = new Graph(n);
        // 添加顶点
        for (String vertexValue : vertexs) {
            graph.insertVertex(vertexValue);
        }
        // 添加边 A-B A-C B-C B-D B-E
        graph.insertEdge(0,1,1);// A-B
        graph.insertEdge(0,2,1);// A-C
        graph.insertEdge(1,2,1);// B-C
        graph.insertEdge(1,3,1);// B-D
        graph.insertEdge(1,4,1);// B-E

        // 显示
        graph.showGraph();

    }


    /**
     * 构造器
     */
    public Graph(int n) {
        // 初始化矩阵和vertexList
        edges = new int[n][n];
        vertexList = new ArrayList<String>(n);
        numOfEdges = 0;
    }

    /**
     * 插入顶点
     * @param vertex
     */
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }

    /**
     * 添加边
     * @param v1 第一个顶点对应的下标
     * @param v2 第二个顶点对应的下标
     * @param weight 权值
     */
    public void insertEdge(int v1,int v2,int weight){
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges ++ ;
    }

    /**
     * 返回顶点个数
     * @return
     */
    public int getNumOfVertex(){
        return vertexList.size();
    }

    /**
     * 返回边的数目
     * @return
     */
    public int getNumOfEdges(){
        return numOfEdges;
    }

    /**
     * 返回顶点i(下标)对应的数据0->'A',1->'B',2->'C'
     * @param index
     * @return
     */
    public String getValueByIndex(int index){
        return vertexList.get(index);
    }

    /**
     * 返回v1,v2的权值
     * @param v1
     * @param v2
     * @return
     */
    public int getWeiht(int v1, int v2){
        return edges[v1][v2];
    }

    /**
     * 显示对应的矩阵
     */
    public void showGraph(){
        for (int[] link : edges) {
            System.out.println(Arrays.toString(link));
        }

    }
}

图的深度优先(DFS)遍历

  1. 深度优先遍历的策略就是首先访问第一个邻接节点,然后再以这个被访问的邻接节点作为初始节点,访问它的第一个邻接节点,可以这样理解:每次都在访问完当前节点后首先访问当前节点的第一个邻接节点。

  2. 我们可以看出,这样的访问策略是优先往纵向挖掘深入,而不是对一个节点的所有邻接节点进行横向访问。

  3. 显然,深度优先搜索是一个递归的过程

深度优先遍历算法步骤

  1. 访问初始节点v,并标记节点v为已经访问

  2. 查找节点v的第一个邻接节点w

  3. 若w存在,则继续执行4,如果w不存在,则回到第1步,将从v的下一个节点继续

  4. 若w未被访问,对w进行深度优先遍历递归(即把w当作另一个v,然后进行步骤123)

  5. 若w已经被访问,查找节点v的w邻接节点的下一个邻接节点,转到步骤3

代码实现:

/**
 * 邻接矩阵 深度优先遍历
 * @author sixiaojie
 * @date 2022-01-07-19:05
 */
public class Graph {
    /** 存储顶点集合 */
    private ArrayList<String> vertexList;

    /** 存储图对应的邻接矩阵 */
    private int[][] edges;

    /** 表示边的数目 */
    private int numOfEdges;

    /** 记录某个节点是否被访问 */
    private boolean[] isVisited;

    public static void main(String[] args) {
        // 顶点个数
        int n = 5;
        String[] vertexs = {"A","B","C","D","E"};
        Graph graph = new Graph(n);
        // 添加顶点
        for (String vertexValue : vertexs) {
            graph.insertVertex(vertexValue);
        }
        // 添加边 A-B A-C B-C B-D B-E
        graph.insertEdge(0,1,1);// A-B
        graph.insertEdge(0,2,1);// A-C
        graph.insertEdge(1,2,1);// B-C
        graph.insertEdge(1,3,1);// B-D
        graph.insertEdge(1,4,1);// B-E

        // 显示
        graph.showGraph();

        // 测试深度优先遍历
        System.out.println("深度优先遍历");
        graph.dfs();
    }


    /**
     * 构造器
     */
    public Graph(int n) {
        // 初始化矩阵和vertexList
        edges = new int[n][n];
        vertexList = new ArrayList<String>(n);
        numOfEdges = 0;
        isVisited = new boolean[n];
    }

    /**
     * 插入顶点
     * @param vertex
     */
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }

    /**
     * 添加边
     * @param v1 第一个顶点对应的下标
     * @param v2 第二个顶点对应的下标
     * @param weight 权值
     */
    public void insertEdge(int v1,int v2,int weight){
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges ++ ;
    }

    /**
     * 返回顶点个数
     * @return
     */
    public int getNumOfVertex(){
        return vertexList.size();
    }

    /**
     * 返回边的数目
     * @return
     */
    public int getNumOfEdges(){
        return numOfEdges;
    }

    /**
     * 返回顶点i(下标)对应的数据0->'A',1->'B',2->'C'
     * @param index
     * @return
     */
    public String getValueByIndex(int index){
        return vertexList.get(index);
    }

    /**
     * 返回v1,v2的权值
     * @param v1
     * @param v2
     * @return
     */
    public int getWeiht(int v1, int v2){
        return edges[v1][v2];
    }

    /**
     * 显示对应的矩阵
     */
    public void showGraph(){
        for (int[] link : edges) {
            System.out.println(Arrays.toString(link));
        }

    }

    /**
     * 获取第一个邻接节点的下标w
     * @param index
     * @return 如果存在就返回对应的下标,不存在返回-1
     */
    public int getFirstNeighbor(int index){
        for (int j = 0; j < vertexList.size(); j++) {
            if (edges[index][j] > 0){
                return j;
            }
        }
        return -1;
    }

    /**
     * 根据前一个邻接节点的下标来获取下一个邻接节点
     * @param v1
     * @param v2
     * @return
     */
    public int getNextNeighbor(int v1,int v2){
        for (int j = v2 + 1; j < vertexList.size()  ; j++) {
            if(edges[v1][j] > 0){
                return j;
            }
        }
        return -1;
    }

    /**
     * 深度优先遍历
     * @param isVisited
     * @param i
     */
    public void dfs(boolean[] isVisited,int i){
        // 首先访问该节点,输出
        System.out.print(getValueByIndex(i) + "->");
        // 将该节点设置为已经访问
        isVisited[i] = true;
        // 查找节点i的第一个邻接节点w
        int w = getFirstNeighbor(i);

        while (w != -1){
            // 没有被访问过
            if(!isVisited[w]){
                dfs(isVisited,w);
            }
            // 已经被访问过,查找节点i的w邻接节点的下一个邻接节点
            w = getNextNeighbor(i,w);
        }
    }

    /**
     * 遍历所有的节点,并进行dfs
     */
    public void dfs(){
        // 遍历所有的节点,进行dfs[回溯]
        for (int i = 0; i < getNumOfVertex(); i++) {
            if(!isVisited[i]){
                dfs(isVisited,i);
            }
        }
    }

}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

每天进步一点点【图的深度优先遍历(DFS)】 的相关文章

随机推荐

  • 源于老师的博客

    Javascript学习 老师的博客 JavaScript概述 ECMAScript和JavaScript的关系 1996年11月 JavaScript的创造者 Netscape公司 决定将JavaScript提交给国际标准化组织ECMA
  • VSCODE(十)C++语言特有设置

    C C 插件为C C 开发者提供了丰富的设置项 包括三个方面的设置 env 用户自定义的变量 可以通过类似
  • 人工神经网络建模过程,人工神经网络建模例子

    利用人工神经网络建立模型的步骤 人工神经网络有很多种 我只会最常用的BP神经网络 不同的网络有不同的结构和不同的学习算法 简单点说 人工神经网络就是一个函数 只是这个函数有别于一般的函数 它比普通的函数多了一个学习的过程 在学习的过程中 它
  • k8s优雅停服

    在应用程序的整个生命周期中 正在运行的 pod 会由于多种原因而终止 在某些情况下 Kubernetes 会因用户输入 例如更新或删除 Deployment 时 而终止 pod 在其他情况下 Kubernetes 需要释放给定节点上的资源时
  • freeswitch录音功能开启无法通话

    freeswitch录音问题 之前开启过代理模式 在dialPlan default xml中添加
  • 初学Android,图形图像之打砖块游戏(二十八)

    这个弹球游戏是没有砖块的打砖块游戏 简版 效果如下 package WangLi Graphics PinBall import java util Random import java util Timer import java uti
  • 操作系统实验七-内存页面置换算法的设计和主存储器空间的分配和回收

    个人博客地址 实验1 内存页面置换算法的设计 一 实验内容 实现最近最久未使用 LRU 置换算法 二 实验目的 LINUX中 为了提高内存利用率 提供了内外存进程对换机制 内存空间的分配和回收均以页为单位进行 一个进程只需将其一部分调入内存
  • 云管平台 — vRealize Suite

    原文地址 https blogs vmware com china 2017 11 08 E4 BA 91 E7 AE A1 E5 B9 B3 E5 8F B0 vrealize suite vRealize Suite 是 vRealiz
  • 取余运算的意义

    取余运算的意义一般是给一个数一个界定范围 就比如m n 100 就限定了m的的范围只能是0 100 更形象来说 我们可以把它想象成一个圆环 我们扩大n 就像当于在0 100这个圈内打转 我们再稍微扩展一下 n 0 while true n
  • C/C++ C++20 格式化库 std::format

    说明 文本格式化库提供 printf 函数族的安全且可扩展的替用品 有意使之补充既存的 C I O 流库并复用其基础设施 例如对用户定义类型重载的流插入运算符 头文件 include
  • npm ERR! missing script dev

    刚刚npm install之后执行npm run dev 出现的报错信息npm ERR missing script dev 1 一种可能时vue init webpack的时候多建了一层文件夹 然后运行的时候没有找到package jso
  • MySQL--udf提权

    udf提权 udf user defined function 即 用户自定义函数 是通过添加新函数 对MYSQL的功能进行扩充 如何获得udf文件 将文件放到哪才能让mysql承认这个函数 函数功能 为什么这东西能提权 自定义函数指令是直
  • Netty-UDP协议

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 实现一个UDP应用关键的点 1 和tcp的不同 udp没有接收的说法 所以即使是接收端 也使用Bootstrap 2 指定channel为NioDatagramChanne
  • 汉字编码输入法综述

    2 汉字编码输入法综述 作者 戴石麟 sbxlm 126 com 本章打算分基础工作 理论研究和实用系统三个方面来对汉字编码输入技术的历史和现状进行综合评述 最后指出现有技术中存在的问题并预测今后技术的发展趋势 2 1基础工作 1974年8
  • java 导入自定义类

    eclipse导入很容易 昨天上课学了一下用记事本写java 导入自定义类 这就麻烦了 代码贴一下 方便操作 package tom jiafei public class SquareEquation double a b c doubl
  • 【SpringMVC】Jrebel 插件实现热部署与文件上传

    目录 一 JRebel 1 1 Jrebel介绍 1 2 Jrebel插件下载 1 3 Jrebel服务下载并启动 1 4 在线生成GUID 1 5 JRebel激活 1 6 相关设置 注意 二 文件上传 下载 2 1 导入pom依赖 2
  • MATLAB 拟合神经网络—— fitnet

    建立神经网络 语法 net fitnet hiddenSizes trainFcn hiddenSize 为隐藏层数 是一个行向量 分别表示从左到右的隐藏层神经元数 trainFcn 为训练函数 如下表所示 名称 函数 trainlm Le
  • go 进阶 go-zero相关: 三. go-zero 微服务基础示例

    目录 一 go zero 微服务基础 安装 ETCD 1 docker 安装运行etcd 2 windows 安装 etcd 二 go zero使用goctl命令创建一个普通的服务 三 go zero使用goctl命令创建一个rpc服务 1
  • python批量下载文件并压缩后上传到owncloud

    目录 1 首先获的一个保存url的文件 2 下载文件到服务器 3 将文件上传到owncloud 3 1 上传单个文件 3 2 上传多个文件 大文件拆分为小文件 推荐 摘要 笔者想下载东西到本地 直接下载速度超慢 一共需要下载1500张图 下
  • 每天进步一点点【图的深度优先遍历(DFS)】

    图是一种数据结构 其中节点可以具有零个或多个相邻元素 两个节点之间的连接称为边 节点也可以称为顶点 图分为三种 无向图 有向图 带权图 图的表示方式有两种 二维数组表示 邻接矩阵 链表表示 邻接表 邻接矩阵 邻接矩阵是表示图形中顶点之间相邻