图结构详解

概述

图是由有穷非空集合的顶点和顶点之间的边组成的集合,通常表示为 G(V,E)。G 表示一个图,V 是 图 G 顶点的集合,E 是图 G 边的集合。在图形结构中,数据之间具有任意联系,任意两个数据之间都可能相关,可用于表示多对多的数据结构

如果从顶点 V1 到 V2 的边没有方向,则称这条边为无向边,顶点和无向边组成图称为无向图。如果从顶点 V1 到 V2 的边有方向,则称这条边为有向边,由顶点和有向边组成的图称为有向图


图的存储结构:邻接矩阵

图的邻接矩阵的存储方式是基于两个数组来表示图的数据结构并存储图中的数据。一个一维数组存储图中的顶点信息,一个二维数组(也叫作邻接矩阵)存储图中的边信息,这里用 /<v1, v2/> 表示顶点 v1 和 v2 的边信息

在无向图的邻接矩阵中,如果 /<v1, v2/> 值为 1,则表示对应的顶点 v1 和 v2 连通,为 0 则不连通。在无向图的邻接矩阵中,主对角元素都为 0,即顶点自身没有连通关系

在有向图的邻接矩阵中,如果 /<v1, v2/> 值为 1,则表示存在从 v1 到 v2 的有向边,为 0 则表示不存在。同样,在有向图的邻接矩阵中,主对角元素都为 0,也就是说从顶点到自身没有连通关系。要注的是,有向图的边是有方向的,v1 的 出度为 2,表示从 v1 顶点出发的边有两条,v2 的出度为 0,表示没有从 v2 出发的边

某些图的每条边都带有权重,如果要将这些权值保存下来。则可以采用权值代替邻接矩阵中的 0、1。有权值代表存在边,无权值则用其他特殊符号表示


图的存储结构:邻接表

邻接表采用了数组与链表相结合的存储方式,是图的一种链式存储结构,主要用于解决邻接矩阵中顶点多边少时存在空间浪费的问题,具体的处理方法如下:

  1. 将图中的顶点信息存储在一个一维数组中,同时在顶点信息中存储用于指向第 1 个邻节点的指针,以便查找该顶点的边信息
  2. 图中每个顶点 V 的所有邻节点构成一个线性表,由于邻节点的个数不定,所以用单向链表存储。在单向链表中,每一个邻节点都保存有自身在一维数组中的下标,以及指向下一个邻节点的指针

对于带权值的图,在节点定义中再增加一个权重值 weight 的数据域,存储权值信息即可


图的遍历

图的遍历指从图中某一顶点出发遍访图中的每个顶点,且使每个原点仅被访问一次。图的遍历分为广度优先遍历和深度优先遍历,且对无向图和有向图都适用

广度优先遍历:假设从图中某个顶点 V 出发,在访问 V 之后依次访问 V 的各个未曾访问的邻节点,然后分别从这些邻节点出发依次访问它们的邻节点;如果此时图中尚有节点未被访问,则另选一个未曾访问的节点作为起点重复上述过程,直至图中所有节点均被访问

深度优先遍历:假设从图中某个顶点 V 出发,在访问 V 之后再访问它的第一个邻节点,再以这个邻节点为起点,重复上述步骤;如果此时图中尚有节点未被访问,则另选一个未曾访问的节点作为起点重复上述过程,直至图中所有节点都被访问

代码实现如下:

import java.util.*;

public class GraphLoopTest {

    // 使用邻接表来存储图数据
    private Map<String, List<String>> graph = new HashMap<String, List<String>>();

    public void initGraphData() {
        // 初始化图数据,图结构如下
        //          1
        //        /   \
        //       2     3
        //      / \   / \
        //     4  5  6  7
        //      \ | / \ /
        //        8    9
        graph.put("1", Arrays.asList("2", "3"));
        graph.put("2", Arrays.asList("1", "4", "5"));
        graph.put("3", Arrays.asList("1", "6", "7"));
        graph.put("4", Arrays.asList("2", "8"));
        graph.put("5", Arrays.asList("2", "8"));
        graph.put("6", Arrays.asList("3", "8", "9"));
        graph.put("7", Arrays.asList("3", "9"));
        graph.put("8", Arrays.asList("4", "5", "6"));
        graph.put("9", Arrays.asList("6", "7"));
    }

    private Queue<String> queue = new LinkedList<String>();
    private Map<String, Boolean> status = new HashMap<String, Boolean>();

    /**
     * 广度优先遍历
     */
    public void BFSSearch(String startPoint) {
        queue.add(startPoint);
        status.put(startPoint, false);
        bfsLoop();
    }

    private void bfsLoop() {
        // 从 queue 取出队列头的点,更新状态为已经遍历。
        String currentQueueHeader = queue.poll();
        status.put(currentQueueHeader, true);
        System.out.println(currentQueueHeader);

        // 找出与此点邻接且尚未遍历的点,进行标记,然后全部放入queue中
        List<String> neighborPoints = graph.get(currentQueueHeader);
        for (String poinit : neighborPoints) {
            if (!status.getOrDefault(poinit, false)) {
                if (queue.contains(poinit)) continue;
                queue.add(poinit);
                status.put(poinit, false);
            }
        }
        // 如果队列不为空继续遍历
        if (!queue.isEmpty()) {  
            bfsLoop();
        }
    }

    private Stack<String> stack = new Stack<String>();

    /**
     * 深度优先遍历
     */
    public void DFSSearch(String startPoint) {
        stack.push(startPoint);
        status.put(startPoint, true);
        dfsLoop();
    }

    private void dfsLoop() {
        if(stack.empty()){
            return;
        }
        // 查看栈顶元素,但并不出栈
        String stackTopPoint = stack.peek();
        // 找出与此点邻接的且尚未遍历的点,进行标记
        List<String> neighborPoints = graph.get(stackTopPoint);
        for (String point : neighborPoints) {
            if (!status.getOrDefault(point, false)) {
                stack.push(point);
                status.put(point, true);
                dfsLoop();
            }
        }
        String popPoint =  stack.pop();
        System.out.println(popPoint);
    }

    public static void main(String[] args) {
        GraphLoopTest test = new GraphLoopTest();
        test.initGraphData();
        test.BFSSearch("1");
        test.DFSSearch("1");
    }
}

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

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

相关文章

彻底解决 node/npm, Electron下载失败相关问题, 从底层源码详解node electron 加速配置

最近玩了一下electron项目, 总是会遇到electron的下载失败问题, 于是看了一下node源码, 做一个记录. node/npm 加速配置 这个配置通过设置node配置里面的registry 这个配置项来完成加速. 配置方法 npm config set registry https://registry.npmmirror.com上面的命令就是将当…

打破界限,自闭症寄宿学校带给孩子的改变

在社会的广阔画卷中&#xff0c;有一群特别的孩子&#xff0c;他们以独特的视角感知世界&#xff0c;以非凡的方式表达情感&#xff0c;他们就是自闭症儿童。自闭症&#xff0c;这个听起来略带神秘色彩的词汇&#xff0c;实则承载着无数家庭的期盼与挑战。在这片充满爱的土地上…

C++设计模式——Iterator迭代器模式

一&#xff0c;迭代器模式的定义 迭代器模式是一种行为型设计模式&#xff0c;它使得遍历一个容器对象中的元素变得更加简单。 迭代器模式将遍历操作从容器对象&#xff08;如集合、列表&#xff09;中分离出来&#xff0c;它通过迭代器对象来遍历容器对象中的元素&#xff0…

RK3568平台开发系列讲解(PWM篇)使用 sysfs 接口操作 pwm

🚀返回专栏总目录 文章目录 一、查看 pwm 设备信息二、使用 sysfs 操作 PWM沉淀、分享、成长,让自己和他人都能有所收获!😄 PWM 子系统被划分为了三个层次, 分别为用户空间、 内核空间和硬件层, 内核空间包括 PWM 设备驱动层、 PWM 核心层和 PWM 适配器驱动层 一、查看…

JS面试真题 part3

JS面试真题 part3 11、bind、call、apply区别&#xff1f;如何实现一个bind12、JavaScript中执行上下文和执行栈是什么13、说说JavaScript中的事件模型14、解释下什么是事件代理&#xff1f;应用场景&#xff1f;15、说说你对闭包的理解&#xff1f;闭包使用场景 11、bind、cal…

分布式技术概览

文章目录 分布式技术1. 分布式数据库&#xff08;Distributed Databases&#xff09;2. 分布式文件系统&#xff08;Distributed File Systems&#xff09;3. 分布式哈希表&#xff08;Distributed Hash Tables, DHTs&#xff09;4. 分布式缓存&#xff08;Distributed Caching…

面向对象需求分析

1. 面向对象分析概述 1.1 面向对象基本概念 以对象为中心&#xff0c;以类为构造机制&#xff0c;来认识、理解、刻画客观世界和设计、构建相应的软件系统。 1.2 UML统一建模语言 为什么要使用UML UML基本概念 统一建模语言&#xff08;UML&#xff09;是一个支持模型化和软…

【电子通识】半导体工艺——刻蚀工艺

在文章【电子通识】半导体工艺——光刻工艺中我们讲到人们经常将 Photo Lithography&#xff08;光刻&#xff09;缩写成 Photo。光刻工艺是在晶圆上利用光线来照射带有电路图形的光罩&#xff0c;从而绘制电路。光刻工艺类似于洗印黑白照片&#xff0c;将在胶片上形成的图像印…

opencv之图像梯度

图像梯度 图像梯度计算的是图像变化的速度。对于图像的边缘部分&#xff0c;其灰度值变化较大&#xff0c;梯度值也较大&#xff1b;相反&#xff0c;对于图像中比较平滑的部分&#xff0c;其灰度值变化较小&#xff0c;相应的梯度值也较小。一般情况下&#xff0c;图像梯度计…

首批通过!华为云CodeArts Snap智能开发助手通过可信AI智能编码工具评估,获当前最高等级

近日&#xff0c;华为云CodeArts Snap智能开发助手在中国信通院组织的智能编码工具首轮评估中&#xff0c;最终获得4级评级, 成为国内首批通过该项评估并获得当前最高评级的企业之一。 此次评估以《智能化软件工程技术和应用要求 第2部分&#xff1a;智能开发能力》为依据&…

ubuntu 20.04 一直卡在登录界面,即使密码正确也无法登录(失败记录)

ubuntu 20.04 一直卡在登录界面&#xff0c;即使密码正确也无法登录 这次是装实体机&#xff0c;一次失败的尝试。。。 名称型号CPUIntel Xeon E5-2673 V3GPURTX 3060 mobile 安装的时候不要选install third-party software for graphics and Wi-fi hardware and additional …

Leetcode面试经典150题-55.跳跃游戏

解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {public boolean canJump(int[] nums) {/**如果就一个位置&#xff0c;你本来就在这&#xff0c;肯定可以跳到*/if(nums.length 1) {return true;}/**这个题的解题思路是遍历数组&#xff0c;如果当前位置不在之…

每日OJ_牛客_数组中出现次数超过一半的数字

目录 牛客_数组中出现次数超过一半的数字 解析代码1 解析代码2 牛客_数组中出现次数超过一半的数字 数组中出现次数超过一半的数字__牛客网 给一个长度为 n 的数组&#xff0c;数组中有一个数字出现的次数超过数组长度的一半&#xff0c;请找出这个数字。例如输入一个长度为…

RP2040 C SDK clocks时钟源配置使用

RP2040 C SDK clocks时钟源配置使用 &#x1f33f;RP2040时钟源API函数文档&#xff1a;https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#group_hardware_clocks &#x1f341;RP2040时钟树&#xff1a; 系统时钟源可以来自外部时钟输入&#xff08;exte…

程序员如何写笔记并整理资料?

整理笔记 word。没错&#xff0c;我也看了网上一大堆软件&#xff0c;还有git管理等等。个人认为如果笔记只是记录个人的经验积累&#xff0c;一个word就够了&#xff0c;那些notepad&#xff0c;laTex个人觉得不够简练。word。 1.word可以插入任何文件附件(目前最大的word 20…

9.9(QT Day 2)

将day1做的登录界面升级优化【资源文件的添加】 在登录界面的登录取消按钮进行以下设置&#xff1a; 使用手动连接&#xff0c;将登录框中的取消按钮使用第2种方式的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt4版本的连接到自定义…

面试题复习(0902-0909)

1. 完全背包问题 和01背包唯一的区别是&#xff0c;每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09; 代码和01唯一的区别在于j的循环是从小到大&#xff0c;不是从大到小。ij谁在外谁在内层区别不大。 #include <bits/stdc.h> using namespace std…

国产化数据库挑战及发展趋势

非国产数据库如Oracle、MySQL和MSSQL等在某些领域占据重要地位&#xff0c;但国产数据库的市场份额正在逐步提升&#xff0c;特别是在政策支持和市场需求的双重推动下&#xff0c;国产数据库的替代进程正在加速。 一、国产数据库市场规模 2024年中国数据库市场规模预计为543.1亿…

【树和二叉树的相关定义】概念

1.回顾与概览 2.什么是树型结构 3.树的&#xff08;递归&#xff09;定义与基本术语 3.1树的定义 注意&#xff1a;除了根结点以外&#xff0c;任何一个结点都有且仅有一个前驱 3.2树的其他表示方式 3.3树的基本术语 结点&#xff1a;数据元素以及指向子树的分支根结点:非空…

AI基础 L16 Logic Agents I

What is an Agent? • The main point about agents is they are autonomous: capable of acting independently, exhibiting control over their internal state • Thus: an agent is a computer system capable of autonomous action in some environment in order to mee…