计算机基础--->数据结构(1)【图的存储和遍历】

文章目录

    • 图的存储
    • 图的搜索(无向无权图)
      • 代码演示

图中包含 顶点、边、度,无向图,有向图,无权图,带权图,其中 表示一个顶点包含多少条边,有出度和入度。

图的存储

  • 邻接矩阵

在这里插入图片描述
代码

/**
 * 图的存储结构
 */
public class Graph {

    // 存储顶点
    private ArrayList<String> vertextList;
    // 需要一个二维数组,存储邻接矩阵
    private int[][] edges;
    // 边的个数
    private int numofEdges;
    // 顶点是否已经访问
    private static boolean[] isVisited;

    // n: 边数
    public Graph(int n) {
        vertextList = new ArrayList<>();
        isVisited = new boolean[n];
        edges = new int[n][n];
        numofEdges = 0;
    }

    // 如何添加顶点
    public void insertVertex(String vertex) {
        vertextList.add(vertex);
    }

    /**
     * @param e1
     * @param e2
     * @param weight 边的权值,0代表无边,1代表有边
     */
    public void insertEdges(int e1, int e2, int weight) {
        edges[e1][e2] = weight;
        edges[e2][e1] = weight;
        numofEdges++;
    }

    // 返回节点的个数
    public int getNumofVertex() {
        return vertextList.size();
    }

    // 返回边的个数
    public int getNumofEdges() {
        return numofEdges;
    }

    // 获取临界矩阵对应下标的值
    public int getWeight(int e1, int e2) {
        return edges[e1][e2];
    }

    public String getValueByIndex(int index) {
        return vertextList.get(index);
    }

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

    // 获取第一个邻接点的下标
    public int getFirstNeighbor(int index) {
        for (int i = 0; i < vertextList.size(); i++) {
            if (edges[index][i] > 0) {
                return i;
            }
        }
        return -1;
    }

    // 通过前一个邻接节点的下标获取下一个邻接节点
    public int getNextNeighbor(int e1, int e2) {
        for (int i = e2 + 1; i < vertextList.size(); i++) {
            if (edges[e1][i] > 0) {
                return i;
            }
        }
        return -1;
    }

相当于一个二维数组,当存储无向图时,两点相连标1,当存储有向图时,横向的是起始点指向相应的点标1。优点是简单高效,缺点是浪费空间

  • 邻接表存储

在这里插入图片描述

无向图是将每个点相连的点用链表存储,无向图邻接表中元素的边是途中边的2倍。

有向图是将每个点指向的点列出来,有向图邻接表中元素的边与图中的边数相同。

图的搜索(无向无权图)

  • 广度优先搜索(使用队列)

过程:

  1. 初始状态,将原定点放入队列
  2. 取出堆首节点(搜索数据),将这个堆首的后继未访问过的顶点放入队列
  3. 重复2步骤
  4. 最终取出所有的对首节点即为搜索数据

代码

 // 广度优先遍历
    public void bfs(boolean[] ifVisited, int index) {
        // 获得头节点
        int headIndex;
        // 邻接点w
        int w;
        // 用队列模拟操作
        LinkedList<Integer> linkedList = new LinkedList<>();

        // 输出当前节点
        System.out.print(getValueByIndex(index) + "->");

        // 节点访问,进行标记
        isVisited[index] = true;
        // 队列中存入下标
        linkedList.add(index);

        // 当队列非空,就继续执行,负责结束。
        while (!linkedList.isEmpty()) {

            // 获取头节点下标
            headIndex = linkedList.removeFirst();
            // 获取邻接点下标
            w = getFirstNeighbor(headIndex);

            while (w > 0) {
                // 若邻接点w存在,访问邻接点,并标记已访问,节点w入队列
                while (!isVisited[w]) {
                    System.out.print(getValueByIndex(w) + "->");
                    isVisited[w] = true;
                    linkedList.add(w);
                }
                // 下一个邻接节点要出来
                w = getNextNeighbor(headIndex, w);
            }

        }
    }

    public void bfs() {
        isVisited = new boolean[getNumofVertex()];
        for (int i = 0; i < getNumofVertex(); i++) {
            if (!isVisited[i]) {
                bfs(isVisited, i);
            }
        }
    }
  • 深度优先搜索(使用栈)

过程:

  1. 初始状态,将原定点放入栈中
  2. 取出栈顶元素(搜索栈),将这个栈顶的后继未访问过的顶点放入栈
  3. 重复2步骤
  4. 最终取出所有的栈顶元素即为搜索数据

代码

   // 深度优先遍历
    public void dfs(boolean[] isVisited, int index) {
        System.out.print(getValueByIndex(index) + "->");
        // 如果为true,表示当前节点已经访问
        isVisited[index] = true;
        // 获取邻接点的下标
        int w = getFirstNeighbor(index);
        while (w != -1) {
            if (!isVisited[w]) {
                dfs(isVisited, w);
            }
            w = getNextNeighbor(index, w);
        }
    }

    // 深度遍历时需要回溯
    public void dfs() {
        for (int i = 0; i < getNumofVertex(); i++) {
            if (!isVisited[i]) {
                dfs(isVisited, i);
            }
        }
    }

在这里插入图片描述

代码演示


import java.util.*;

/**
 * 图的存储结构
 */
public class Graph {

    // 存储顶点
    private ArrayList<String> vertextList;
    // 需要一个二维数组,存储邻接矩阵
    private int[][] edges;
    // 边的个数
    private int numofEdges;
    // 顶点是否已经访问
    private static boolean[] isVisited;

    // n: 边数
    public Graph(int n) {
        vertextList = new ArrayList<>();
        isVisited = new boolean[n];
        edges = new int[n][n];
        numofEdges = 0;
    }

    // 如何添加顶点
    public void insertVertex(String vertex) {
        vertextList.add(vertex);
    }

    /**
     * @param e1
     * @param e2
     * @param weight 边的权值,0代表无边,1代表有边
     */
    public void insertEdges(int e1, int e2, int weight) {
        edges[e1][e2] = weight;
        edges[e2][e1] = weight;
        numofEdges++;
    }

    // 返回节点的个数
    public int getNumofVertex() {
        return vertextList.size();
    }

    // 返回边的个数
    public int getNumofEdges() {
        return numofEdges;
    }

    // 获取临界矩阵对应下标的值
    public int getWeight(int e1, int e2) {
        return edges[e1][e2];
    }

    public String getValueByIndex(int index) {
        return vertextList.get(index);
    }

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

    // 获取第一个邻接点的下标
    public int getFirstNeighbor(int index) {
        for (int i = 0; i < vertextList.size(); i++) {
            if (edges[index][i] > 0) {
                return i;
            }
        }
        return -1;
    }

    // 通过前一个邻接节点的下标获取下一个邻接节点
    public int getNextNeighbor(int e1, int e2) {
        for (int i = e2 + 1; i < vertextList.size(); i++) {
            if (edges[e1][i] > 0) {
                return i;
            }
        }
        return -1;
    }

    // 深度优先遍历
    public void dfs(boolean[] isVisited, int index) {
        System.out.print(getValueByIndex(index) + "->");
        // 如果为true,表示当前节点已经访问
        isVisited[index] = true;
        // 获取邻接点的下标
        int w = getFirstNeighbor(index);
        while (w != -1) {
            if (!isVisited[w]) {
                dfs(isVisited, w);
            }
            w = getNextNeighbor(index, w);
        }
    }

    // 深度遍历时需要回溯
    public void dfs() {
        for (int i = 0; i < getNumofVertex(); i++) {
            if (!isVisited[i]) {
                dfs(isVisited, i);
            }
        }
    }

    // 广度优先遍历
    public void bfs(boolean[] ifVisited, int index) {
        // 获得头节点
        int headIndex;
        // 邻接点w
        int w;
        // 用队列模拟操作
        LinkedList<Integer> linkedList = new LinkedList<>();

        // 输出当前节点
        System.out.print(getValueByIndex(index) + "->");

        // 节点访问,进行标记
        isVisited[index] = true;
        // 队列中存入下标
        linkedList.add(index);

        // 当队列非空,就继续执行,负责结束。
        while (!linkedList.isEmpty()) {

            // 获取头节点下标
            headIndex = linkedList.removeFirst();
            // 获取邻接点下标
            w = getFirstNeighbor(headIndex);

            while (w > 0) {
                // 若邻接点w存在,访问邻接点,并标记已访问,节点w入队列
                while (!isVisited[w]) {
                    System.out.print(getValueByIndex(w) + "->");
                    isVisited[w] = true;
                    linkedList.add(w);
                }
                // 下一个邻接节点要出来
                w = getNextNeighbor(headIndex, w);
            }

        }
    }

    public void bfs() {
        isVisited = new boolean[getNumofVertex()];
        for (int i = 0; i < getNumofVertex(); i++) {
            if (!isVisited[i]) {
                bfs(isVisited, i);
            }
        }
    }


    public static void main(String[] args) {

        int n = 4;
        Graph graph = new Graph(n);

        // 添加顶点
        graph.insertVertex("A");
        graph.insertVertex("B");
        graph.insertVertex("C");
        graph.insertVertex("D");

        // 添加边
        graph.insertEdges(1, 0, 1);
        graph.insertEdges(1, 3, 1);
        graph.insertEdges(3, 0, 1);
        graph.insertEdges(3, 2, 1);

        graph.showGraph();
        System.out.println("深度优先遍历");
        graph.dfs();
        System.out.println("\n广度优先遍历");
        graph.bfs();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/19730.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

(CVE-2022-22965)Spring Framework 远程命令执行漏洞(vulfocus复现)

漏洞原理 该漏洞是SpringFramework数据绑定的一个漏洞&#xff0c;如果后台方法中接受的参数为非基础类型&#xff0c;Spring会根据前端传入的请求正文中的参数的key值来查询与其名称所对应的getter和setter方法&#xff0c;攻击者利用这一特性修改了Tomcat的一个用于日志记录…

浅谈Android PMS解析APP信息流程

前言 前面我们了解了Zygote的启动流程&#xff0c;知道AMS、PMS都是由SystemServer进程启动的&#xff0c;我们都知道PMS主要负责App管理工作&#xff0c;这里我们简单从源码角度分析下PMS是如何解析APP解析的&#xff1b; 源码分析(API 30为例) 我们还是从PackageManagerSe…

英文论文润色哪家好用比较好,有值得推荐的吗

英文论文润色 推荐 英文论文润色对于写作者来说是一项十分重要的任务&#xff0c;它可以帮助我们修改文章中的语法、标点和排版等问题&#xff0c;使论文更加准确和易读。在众多的英文润色软件中&#xff0c;147chatgpt改写润色软件是一款值得推荐的全自动批量图文润色、自动纠…

财报解读:Q2业绩指引未达预期,狂奔的爱彼迎要减速了?

全球民宿龙头爱彼迎Airbnb迎来了一个强劲的开端。 美东时间5月9日盘后&#xff0c;爱彼迎发布了2023年第一季度财报。财报显示&#xff0c;爱彼迎一季度营收、净利润、总预订金额都获得了不同程度增长&#xff0c;超出市场预期。美中不足的是&#xff0c;公司预计二季度营收下…

对接银行处理退票的解决方案

什么是退票&#xff1f; 在跨行支付时&#xff0c;付款请求提交汇出行后&#xff0c;由汇出行转交至人民银行支付系统&#xff0c;经人民银行大小额系统处理后会先返回交易成功的结果&#xff0c;再由人民银行转至收款行&#xff0c;收款行在清算过程中会将收款人账户信息、状…

战略投资奥琦玮,微盟冲在餐饮复苏最前线

作者 | 辰纹 来源 | 洞见新研社 好起来了&#xff0c;一切都好起来了。 刚刚过去的五一假期&#xff0c;广州费大厨正佳广场店每天取号1000多桌&#xff0c;餐厅翻台率达到了1200%&#xff1b;长沙文和友单日最高排号超过1万&#xff0c;到店人数近6万&#xff1b;武汉主力龙…

【无标题】c++异常机制的一些总结以及思考

在谈及c处理异常机制的方法之前我们不妨来回顾一下c语言是如何应对这块的。 终止程序&#xff0c;如assert&#xff0c;缺陷&#xff1a;用户难以接受。如发生内存错误&#xff0c;除0错误时就会终止程序。 返回错误码&#xff0c;缺陷&#xff1a;需要程序员自己去查找对应的…

输入url后,到页面展示出来

目录 1、用户在浏览器中输入url地址 2、缓存解析 3、浏览器进行DNS解析域名得到服务器ip地址 4、TCP三次握手建立客户端和服务器的连接 5、客户端发送HTTP请求获取服务器端的静态资源 6、服务器发送HTTP响应报文给客户端&#xff0c;客户端获取到页面静态资源 7、TCP四次…

快速了解 TypeScript

目录 1、简介 2、安装TypeScript 3、编译代码 4、类型注解 5、接口 6、类 7、运行TypeScript Web应用 1、简介 TypeScript是JavaScript类型的超集&#xff0c;它可以编译成纯JavaScript。 TypeScript可以在任何浏览器、任何计算机和任何操作系统上运行&#xff0c;并且…

LeetCode_Day2 | 有意思的数组滑动窗口及螺旋矩阵

LeetCode_数组 977.有序数组的平方1.题目描述2.暴力法3. 双指针法 209.长度最小的子数组1.题目描述2.暴力法3.滑动窗口(双指针法) 59.螺旋矩阵1.题目描述2. 螺旋矩阵解法 977.有序数组的平方 1.题目描述 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字…

推荐6个我经常逛的“小网站”,嘿嘿嘿!!!

如今&#xff0c;全球互联网上已经有超过 17 亿个网站。除了全球那些主流网站被大家所熟知外&#xff0c;其实还有很多很多网站&#xff0c;被淹没在了互联网世界中。 每次发现优质的内容都会第一时间给大家分享出来&#xff0c;不管是软件&#xff0c;插件&#xff0c;脚本还…

为什么要做计划跟踪:没有计划,就没有控制

日常工作中&#xff0c;我们每天都被大量的信息和任务填满&#xff0c;常常由于任务繁冗复杂&#xff0c;让人陷入一种无所适从的状态。 我们经常会看到很多如何安排工作计划的教程&#xff0c;比如&#xff1a; 要把大的项目分解为小目标&#xff0c;小目目标再分解为日常任务…

【iOS】—— 实现WebSocket发送消息(SocketRocket第三方库的使用和解析)

文章目录 WebSocketWebSocket特点 SocketRocket导入头文件设置代理SRWebSocket的初始化和建立连接SRWebSocketDelegate 代理方法实现加上简单UI实现两个用户之间简单通信浅看了一点点源码&#xff08;理解的不深&#xff09; 偶然之间了解到了利用WebSocket实现后端和前端的相互…

获取两个日期间时长 (XX天XX时XX分)

使用场景&#xff1a; 发货日期与到货日期 计算运输时长 代码&#xff1a; private String getMinuteTime(String startTime, String endTime) {String minuteTime null;if (StrUtil.isNotBlank(startTime) && StrUtil.isNotBlank(endTime)) {long minute DateUti…

华为OD机试真题 Java 实现【猜字谜】【2023Q1 100分】

一、题目描述 小王设计了一人简单的清字谈游戏&#xff0c;游戏的迷面是一人错误的单词&#xff0c;比如nesw&#xff0c;玩家需要猜出谈底库中正确的单词。猜中的要求如 对于某个谜面和谜底单词&#xff0c;满足下面任一条件都表示猜中&#xff1a; 变换顺序以后一样的&…

寅家科技完成近亿元B1轮融资,加速高阶智能驾驶布局

近日&#xff0c;寅家科技宣布完成近亿元人民币B1轮融资&#xff0c;本轮融资由东方富海、深创投、深圳高新投联合投资&#xff0c;所募资金主要用于公司高阶智能驾驶技术产品的研发迭代&#xff0c;以及智能驾驶产品量产、海外市场开拓&#xff0c;从而进一步提升核心产品的市…

【重新定义matlab强大系列三】MATLAB清洗离群数据(查找、填充或删除离群值)

&#x1f517; 运行环境&#xff1a;matlab &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f91…

应届生如何在职场中提高竞争力?这些方法和策略不容错过!

当前就业形势严峻&#xff0c;对于即将步入职场的应届生来说&#xff0c;提高自己的竞争力显得尤为重要。那么&#xff0c;要如何提高自己的职场竞争力呢&#xff1f;本文将为你分享一些有效的方法和策略&#xff0c;帮助你在职场中获得更好的发展。 一、提高自身素质 职场中&…

关于ADC的笔记1

ADC&#xff0c;全称Anlog-to-Digital Converter&#xff0c;模拟/数字转换器。是指将连续变量的模拟信号转换为离散的数字信号的器件&#xff0c;我们能通过ADC将外界的电压值读入我们的单片机中. 常见的ADC有两种 1.并联比较型&#xff1a; 它的优点是转换速度最快&#x…

VMware 产品下载汇总 2023 持续更新中

本站 VMware 产品下载汇总&#xff1a;vSphere、NSX、Tanzu、Aria、Cloud… 请访问原文链接&#xff1a;https://sysin.org/blog/vmware/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 本站提供的 VMware 软件全部为 “试用版…