Java——图

news2025/7/15 1:48:26

概念

图是由顶点和边组成的一种数据结构,我们之前介绍的树形结构中,树的每一个节点就是顶点,顶点与顶点中的连线就是边,也就是说,树是一种特殊的图

图中的边如果有方向,那么这个图就称为有向图,反之则成为无向图

完全图

如果一个无向图中任意两个节点之间有且仅有一条边,那么称这个图为无向完全图,而一个有向图中,任意两个节点间有且仅有方向相反的边,称之为有向完全图

下图中左侧为无向完全图,右侧为有向完全图
在这里插入图片描述

顶点的度

指的是有几条边与该节点关联,如果是完全图的话,那么分为入度和出度,箭头指向该节点的是这个节点的入度,反之为出度

下图中左侧的所有节点的度都是2,右侧所有节点的入度都是2,出度都是2,也即是所有节点的度都是4
在这里插入图片描述

路径

一个节点到另一个节点要走的边称之为路径,如果这些边上有权值(例如一个节点是北京,一个是天津,一个是上海,那么北京到天津的权值和北京到上海的权值是不一样的)
对于无向图,两个节点间的路径长度等于边的个数
对于有向图,两个节点间的路径长度等于所有边的权值之和

回路和环

对于一个路径,如果起点和终点都一样,那么称这个路径为回路,而如果这个路径的长度为一,也就是这个边是自己指向自己,称之为环

在这里插入图片描述
上图中黑色的三条边组成回路,红色的是环

子图

对于图G,如果图G1的节点都属于图G,边都属于图G,则称G1为G的子图
在这里插入图片描述
例如上图,右面的图就是左侧的图的子图

连通图

如果无向图中任意两个节点间都是有一条路径的,则称这个图是连通图

如果有向图中任意两个节点间都是有一条从起始点到终止点的路径,还有一条从终止点到起始点的路径,则称这个图是强连通图

一个连通图的最小连通子图是该图的生成树

图的存储

在计算机中,主要有下面两种方法来存储图

邻接矩阵

使用矩阵(二维数组来存储节点与节点之间的关系)

对于无向图,两个节点之间如果有边,就是1,或者是边的权值,没有就是0,自己和自己是0,因此,无向图的邻接矩阵表示是关于对角线对称的

对于有向图,一个节点如果有指向另一个节点的边,就是1或者是边的权值,否则就是正无穷,自己和自己是0

在这里插入图片描述

代码实现

定义一个最大值代表正无穷

public class Constant {
    public static final int MAX = Integer.MAX_VALUE;
}

import java.util.Arrays;

/**
 * 使用邻接矩阵存储图
 */
public class GraphByMatrix {
    private char[] arrayV;//顶点数组
    private int[][] matrix;//邻接矩阵
    private boolean isDirect;//是否为有向图

    /**
     * @param size 当前矩阵的顶点个数
     * @param isDirect
     */
    public GraphByMatrix(int size, boolean isDirect){
        this.arrayV = new char[size];
        matrix = new int[size][size];
        for (int i = 0; i < size; i++) {
            Arrays.fill(matrix[i],Constant.MAX);
        }
        this.isDirect = isDirect;
    }

    /**
     * 初始化顶点数组
     * @param array
     */
    public void initArrayV(char[] array){
        for (int i = 0; i < arrayV.length; i++) {
            arrayV[i] = array[i];
        }
    }

    /**
     * 添加边
     * @param srcV 起点
     * @param destV 终点
     * @param weight 权值
     */
    public void addEdge(char srcV, char destV, int weight){
        int srcIndex = getIndexOfV(srcV);
        int destIndex = getIndexOfV(destV);

        matrix[srcIndex][destIndex] = weight;

        //无向图的对称位置也有对应的权值
        if(!isDirect){
            matrix[destIndex][srcIndex] = weight;
        }
    }

    /**
     * 获取v顶点的下标
     * @param v
     * @return
     */
    private int getIndexOfV(char v){
        for (int i = 0; i < arrayV.length; i++) {
            if (arrayV[i] == v){
                return i;
            }
        }
        return -1;
    }

    /**
     * 获取顶点的度
     * 有向图为入度+出度
     * @param v
     * @return
     */
    public int getDevOfV(char v){
        int count = 0;
        int srcIndex = getIndexOfV(v);

        //计算出度
        for (int i = 0; i < arrayV.length; i++) {
            if(matrix[srcIndex][i] != Constant.MAX){
                count++;
            }
        }

        //计算入度
        if(isDirect){
            for (int i = 0; i < arrayV.length; i++) {
                if(matrix[i][srcIndex] != Constant.MAX){
                    count++;
                }
            }
        }

        return count;
    }

    private void printGraph() {
        for (int i = 0; i < arrayV.length; i++) {
            System.out.print(arrayV[i] + " ");
        }
        System.out.println();

        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                if(matrix[i][j] == Constant.MAX){
                    System.out.print("∞ ");
                } else {
                    System.out.print(matrix[i][j] + " ");
                }
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        GraphByMatrix graph = new GraphByMatrix(4,true);
        char[] array = {'A','B','C','D'};

        graph.initArrayV(array);
        graph.addEdge('A','B',1);
        graph.addEdge('A','D',1);
        graph.addEdge('B','A',1);
        graph.addEdge('B','C',1);
        graph.addEdge('C','B',1);
        graph.addEdge('C','D',1);
        graph.addEdge('D','A',1);
        graph.addEdge('D','C',1);

        graph.printGraph();

        System.out.println(graph.getDevOfV('A'));
    }

}

邻接表

使用数组来表示顶点的集合,使用链表来表示边的关系
也就是说,每一个链表中存储了目标顶点所在的下标
在这里插入图片描述
而如果是有向图,那么会存储两个表,一个是入边表,另一个则是出边表

代码实现

import java.util.ArrayList;

/**
 * 使用邻接表存储图
 */
public class GraphByNode {
    static class Node{
        public int src;//起始位置
        public int dest;//目标位置
        public int weight;//权重
        public Node next;

        public Node(int src, int dest, int weight) {
            this.src = src;
            this.dest = dest;
            this.weight = weight;
        }
    }

    public char[] arrayV;//存储顶点
    public ArrayList<Node> edgList;//存储边
    public boolean isDirect;

    public GraphByNode(int size, boolean isDirect){
        this.arrayV = new char[size];
        edgList = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            edgList.add(null);
        }
        this.isDirect = isDirect;
    }

    /**
     * 初始化顶点数组
     * @param array
     */
    public void initArrayV(char[] array){
        for (int i = 0; i < arrayV.length; i++) {
            arrayV[i] = array[i];
        }
    }

    /**
     * 添加边
     * @param srcV
     * @param destV
     * @param weight
     */
    public void addEdge(char srcV, char destV, int weight){
        int srcIndex = getIndexOfV(srcV);
        int destIndex = getIndexOfV(destV);
        addEdgeChild(srcIndex,destIndex,weight);

        if(!isDirect){
            addEdgeChild(destIndex,srcIndex,weight);
        }
    }

    private void addEdgeChild(int srcIndex, int destIndex, int weight){
        Node cur = edgList.get(srcIndex);
        while(cur != null){
            if(cur.dest == destIndex){
                return;
            }
            cur = cur.next;
        }

        //之前没有存储过这条边
        //头插法
        Node node = new Node(srcIndex,destIndex,weight);
        node.next = edgList.get(srcIndex);
        edgList.set(srcIndex,node);
    }

    /**
     * 获取v顶点的下标
     * @param v
     * @return
     */
    private int getIndexOfV(char v){
        for (int i = 0; i < arrayV.length; i++) {
            if (arrayV[i] == v){
                return i;
            }
        }
        return -1;
    }

    /**
     * 获取顶点的度
     * @param v
     * @return
     */
    public int getDevOfV(char v){
        int count = 0;
        int srcIndex = getIndexOfV(v);
        Node cur = edgList.get(srcIndex);

        while(cur != null){
            count++;
            cur = cur.next;
        }

        if(isDirect){
            int destIndex = srcIndex;
            for (int i = 0; i < arrayV.length; i++) {
                if(i == destIndex){
                    continue;
                } else {
                    Node pCur = edgList.get(i);
                    while(pCur != null){
                        if(pCur.dest == destIndex){
                            count++;
                        }
                        pCur = pCur.next;
                    }
                }
            }
        }

        return count;
    }

    public void printGraph(){
        for (int i = 0; i < arrayV.length; i++) {
            System.out.println(arrayV[i] + " ->");
            Node cur = edgList.get(i);
            while(cur != null){
                System.out.print(arrayV[cur.dest] + " ->");
                cur = cur.next;
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        GraphByNode graph = new GraphByNode(4,true);
        char[] array = {'A','B','C','D'};

        graph.initArrayV(array);
        graph.addEdge('A','B',1);
        graph.addEdge('A','D',1);
        graph.addEdge('B','A',1);
        graph.addEdge('B','C',1);
        graph.addEdge('C','B',1);
        graph.addEdge('C','D',1);
        graph.addEdge('D','A',1);
        graph.addEdge('D','C',1);

        graph.printGraph();
        System.out.println(graph.getDevOfV('A'));
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

个人总结详细版的C++调用Opencv和Halcon封装dll

一、前言&#xff1a; 在C调用opencv和Halcon封装的过程中踩过很多坑&#xff0c;然而网上却查不到清晰地教程。在此个人总结详细教程&#xff0c;以免后人踩坑。记录下&#xff0c;以后自己忘了也可以来看看。 二、教程细节 2.1 我使用的IDE是vs2017,下面所有的介绍也都是以此…

理解操作系统(Linux)

操作系统是一款对软硬件资源进行管理的软件&#xff01; 操作系统为什么要对软硬件资源进行管理呢&#xff1f; 操作系统通过合理的管理软硬件资源的手段&#xff0c;为用户提供良好的&#xff08;稳定的、高效的、安全的&#xff09;执行环境。 操作系统是如何进行管理的呢…

机器学习100天(十七):017 逻辑回归梯度下降

机器学习 100 天,今天讲的是:逻辑回归-梯度下降! 在讲解了逻辑回归的基本原理和损失函数之后,我们来推导逻辑回归模型中参数 w 和 b 的梯度表达式。 我们之前介绍过,计算逻辑回归的代价函数实际上包含了下面三个过程: Z = W T X + b Z=W^TX+b Z=

ASOC系统简析

一 嵌入式音频系统介绍 上图是音频系统的硬件模拟图&#xff0c;声卡通过I2S接口与cpu进行音频数据传输&#xff0c;通过I2C接口与cpu进行控制通讯。 录音数据通路&#xff1a;麦克风---->声卡------I2S------>DMA---->内存&#xff1b; 播放数据通路&#xff1a;内存…

HaaS EDU物联网项目实战:野外救援项目

HaaS EDU K1是一款高颜值、高性能、高集成度的物联网开发板&#xff0c;板载功能强大的4核&#xff08;双核300Mhz M33双核1GHz A7&#xff09;主芯片&#xff0c;2.4G/5G双频Wi-Fi&#xff0c;双模蓝牙&#xff08;经典蓝牙/BLE&#xff09;&#xff0c;并自带丰富的传感器与小…

Exception in thread “main“ java.lang.NoClassDefFoundError

项目场景&#xff1a; 验证继承情况下子类创建对象时&#xff0c;先调用父类的构造方法&#xff0c;再调用子类的构造方法 问题描述 随机&#xff08;不同次数的测试下&#xff09;会产生Exception in thread "main" java.lang.NoClassDefFoundError错误 package e…

Pandas 详解

本文主要介绍python 数据分析模块 Pandas&#xff0c;并试图对其进行一个详尽的介绍。 通过阅读本文&#xff0c;你可以&#xff1a; 掌握 Anaconda 环境的安装及使用了解什么是 Pandas掌握 Series 对象基本操作掌握 DataFrame 对象的基本操作掌握缺值处理掌握 Series 对象和 D…

计算机今年炸了?现在还适合入行吗?

恍惚之间&#xff0c;一年已来到了尾声&#xff0c;对于应届生来说&#xff0c;这是不堪回首的一年&#xff0c;一个字&#xff0c;难。 不但冷门专业就业困难&#xff0c;就连过去的宇宙机专业&#xff0c;也不复盛况&#xff0c;相较于往年的火热&#xff0c;今年的计算机行…

ES6的相关知识点

一&#xff1a;var let const let var区别&#xff1a; 1.let没有变量提升&#xff08;预解析&#xff09;我们先定义再使用&#xff0c;Var可以先使用再定义因为他有预解析,var有声明提升>先上车后买票&#xff0c; 2.let不能在同一个作用域中重复定义变量&#xff0c;如…

使用docker容器部署httpd(绝对可用)

众所周知&#xff0c;httpd是一个服务&#xff0c;里面有很多配置要改&#xff0c;通过dockerfile编写很复杂&#xff0c;所以这里&#xff0c;我们【采用进入容器内修改的方式&#xff0c;去生成一个新的httpd容器】 httpd我们平常用yum安装&#xff0c;所以一定要最好是自带…

【小程序】全局配置window和tabBar

目录 全局配置 1. 全局配置文件及常用的配置项 全局配置 - window 1. 小程序窗口的组成部分 2. 了解 window 节点常用的配置项 ​编辑 3. 设置导航栏的标题 4. 设置导航栏的背景色 5. 设置导航栏的标题颜色 6. 全局开启下拉刷新功能 7. 设置下拉刷新时窗口的背景色 …

Hibernate-Validator(数据校验框架)

目录一、Hibernate-Validator 简介二、项目中为什么要用校验框架三、添加依赖四、看一个入门级案例五、常用注解六、使用groups的校验一、Hibernate-Validator 简介 hibernate-validator是Hibernate项目中的一个数据校验框架&#xff0c;它能够将数据校验从业务代码中脱离出来…

-防火墙-

数据来源 一、防火墙的基本概念 防火墙的定义&#xff1a;是一款具备安全防护功能网络设备 ◆ 隔离网络 将需要保护的网络与不可信任网络进行隔离&#xff0c;隐藏信息并进行安全防护 防火墙基本功能&#xff1a; ◆ 访问控制 - ACL ◆ 攻击防护 ◆ 冗余设计 ◆ 路由、交…

基于XMC4800 Ethercat从站的工厂自动化解决方案

背景 随着“工业4.0”与“工业互联网”概念的提出&#xff0c;“互联网制造”的概念被大众所熟知&#xff0c;人们称之为第四次工业革命。在这样的背景下&#xff0c;新的工业自动化孕育而生。智能工厂、提供智能工厂设计与实施的工业4.0解决方案商、技术供应商相继出现。ARRO…

Android---Banner轮播图

轮播图是一种很常见的UI。Banner框架能够帮助我们快速开发&#xff0c;完成首页轮播图效果的需求。 1、导入Banner依赖 implementation io.github.youth5201314:banner:2.2.2 2、activity_main.xml布局。 banner_loop_time: 设置轮播间隔时间&#xff0c;默认3000&#xff…

逆天了!用Numpy开发深度学习框架,透视神经网络训练过程

哈喽&#xff0c;大家好。 今天给大家分享一个非常牛逼的开源项目&#xff0c;用Numpy开发了一个深度学习框架&#xff0c;语法与 Pytorch 基本一致。 今天以一个简单的卷积神经网络为例&#xff0c;分析神经网络训练过程中&#xff0c;涉及的前向传播、反向传播、参数优化等核…

制作圣诞帽其实特简单(附 Python 代码)

圣诞将至&#xff0c;虽然咱不过这洋节&#xff0c;但是热闹还是要凑一下的&#xff0c;相信已经有很多圣诞帽相关的周边在流传了&#xff0c;今天咱们就自己动手&#xff0c;给头像增加一个圣诞帽。 文章目录基础知识准备数字图像图像通道ROI和mask矩阵&#xff08;Numpy&…

BaseAdapter实现的投票案例

BaseAdapter实现的投票案例 1.知识补充 android:descendantFocusability"blocksDescendants"&#xff0c;关键是让谁先去获取焦点beforeDescendants&#xff1a;viewgroup会优先其子类控件而获取到焦点afterDescendants&#xff1a;viewgroup只有当其子类控件不需要获…

Flink Process Function

处理函数: ProcessFunction: 含有状态流的特性 处理函数面对的是数据流中的最基本元素: 数据事件 event, 状态 state, 时间 time 文章目录1.基本处理函数 ProcessFunction1.1 处理函数的功能和使用1.2 ProcessFunction 解析2.处理函数的分类2.1 按键分区处理函数 KeyedProces…

LaTex期刊模板下载与使用

1 LaTex期刊模板下载与使用 接上文介绍了LaTex的下载安装和基本语法使用规则。 上文地址&#xff1a;科研人快速入门LaTex到日常使用&#xff0c;下载安装配置&#xff0c;语法使用说明等 一般来说&#xff0c;LaTeX主要用在论文提交&#xff0c;书籍排版过程中&#xff0c;提…