数据结构之图

news2024/11/15 4:35:39

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7 图的存储

(1)图的邻接矩阵存储
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

对于无向图,邻接矩阵第i行/列上非零元素个数是顶点vi的度。
对于有向图,邻接矩阵第i行上非零元素个数是顶点vi的出度,第i列
上非零元素个数是顶点vi的入度。
对于带权有向图有边则存储权值,无边存储无穷符号,0代表节点相等
邻接矩阵存储图的性能分析:
时间上:判断两顶点间是否右边、获取或设置边的权值等操作花费的时间是O(1)插入或删除元素,需要移动大量元素,效率很低
空间上:不管顶点间是否右边,邻接矩阵中都要占用一个存储单元,存储空间大小为n*n,

(2)图的邻接表存储
在这里插入图片描述
在这里插入图片描述
有向图的邻接表,每条边只存储一次,根据边的方向,分为邻接表(点在边单链表作为起点),逆邻接表(点在边单链表作为终点)
一个图的邻接表表示不唯一,这是因为,边单链表结点的链接次序取决于建立邻接表的算法以及边的输入次序

7 图的遍历

定义:是指从图中任意一个顶点出发,沿着图中的边前行,到达并访问图中的所有顶点,且每个顶点仅被访问一次
(1)深度优先搜索(Depth First Search,DFS)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
连通图与非连通图的深度优先遍历实现

//从顶点vi出发的一次深度优先搜索,遍历一个连通分量;visited指定访问标记数组
    private void depthfs(int i, boolean[] visited)
    {
        System.out.print(this.getVertex(i)+" "); //访问顶点vi
        visited[i] = true; //设置访问标记
        for (int j=this.next(i,-1); j!=-1; j=this.next(i,j)) //j依次获得vi的所有邻接顶点序号
            if(!visited[j]) //若邻接顶点vj未被访问
                depthfs(j, visited); //从vj出发的深度优先搜索遍历,递归调用
    }

    public void DFSTraverse(int i) //非连通图的深度优先搜索遍历,从顶点vi出发
    {
        boolean[] visited=new boolean[this.vertexCount()]; //访问标记数组,元素初值为false,表示未被访问
        int j=i;
        do
        { if (!visited[j]) //若顶点vj未被访问。若i越界,Java将抛出数组下标序号越界异常
        {
            System.out.print("{ ");
            this.depthfs(j, visited); //从顶点vj出发的一次深度优先搜索
            System.out.print("} ");
        }
            j = (j+1) % this.vertexCount(); //在其他连通分量中寻找未被访问顶点
        } while (j!=i);
        System.out.println();
    }

(2) 广度优先搜索(Breadth First Search,BFS)
在这里插入图片描述
在这里插入图片描述
广度优先搜索遍历算法实现

  //从顶点vi出发的一次广度优先搜索,遍历一个连通分量,使用队列
    private void breadthfs(int i, boolean[] visited) {
        System.out.print(this.getVertex(i) + " "); //访问顶点vi
        visited[i] = true; //设置访问标记
        LinkedQueue<Integer> que = new LinkedQueue<Integer>(); //创建链式队列
        que.add(i); //访问过的顶点vi序号入队
        while (!que.isEmpty()) //当队列不空时循环
        {
            i = que.poll(); //出队
            for (int j = next(i, -1); j != -1; j = next(i, j)) //j依次获得vi的所有邻接顶点
                if (!visited[j]) //若顶点vj未访问过
                {
                    System.out.print(this.getVertex(j) + " "); //访问顶点vj
                    visited[j] = true;
                    que.add(j); //访问过的顶点vj序号入队
                }
        }
    }

    public void BFSTraverse(int i) //非连通图的广度优先搜索遍历,从顶点vi出发
    {
        boolean[] visited = new boolean[this.vertexCount()]; //访问标记数组
        int j=i;
        do
        { if (!visited[j]) //若顶点vj未被访问
        {
            System.out.print("{ ");
            breadthfs(j, visited); //从vj出发的一次广度优先搜索
            System.out.print("} ");
        }
            j = (j+1) % this.vertexCount(); //在其他连通分量中寻找未被访问顶点
        } while (j!=i);
        System.out.println();
    }

7 最小生成树

在这里插入图片描述
最小的生成树的概念可以应用到许多实际问题中。例如,以尽可能低的造价建造若干条高速公路,把n个城市联系在一起,就是一个最小生成树问题。
最小生成树的构造方法
(1)Prim算法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
// Prim算法实现

  public void minSpanTree()
    {
        Triple[] mst = new Triple[vertexCount()-1]; //最小生成树的边集合,边数为n-1
        for (int i=0; i<mst.length; i++) //边集合初始化,从顶点v0出发构造
            mst[i]=new Triple(0,i+1,this.weight(0,i+1)); //保存从v0到其他各顶点的边
        for (int i=0; i<mst.length; i++) //选择n-1条边,每趟确定一条权值最小的边
        {
            int minweight=mst[i].value, min=i; //最小权值及边的下标
            for (int j=i+1; j<mst.length; j++) //在i~n-1范围内,寻找权值最小的边
                if (mst[j].value < minweight) //若存在更小权值,则更新最小值变量
                {
                    minweight = mst[j].value; //最小权值
                    min = j; //保存当前权值最小边的序号
                }
            Triple edge = mst[min]; //将权值最小的边(由min记得)交换到第i个元素,表示该边加入TE集合
            if (min!=i)
            {
                mst[min] = mst[i];
                mst[i] = edge;
            }

            //将i+1~n-1的其他边用权值更小的边替换
            int tv = edge.column; //刚并入TV的顶点
            for (int j=i+1; j<mst.length; j++)
            {
                int v = mst[j].column; //原边在V-TV中的终点
                int weight = this.weight(tv,v);
                if (weight<mst[j].value) //若(tv,v)边比第j条边的权值更小,则替换
                    mst[j] = new Triple(tv,v,weight);
            }
        }

        System.out.print("\n最小生成树的边集合:");
        int mincost=0;
        for (int i=0; i<mst.length; i++) //输出最小生成树的边集合和代价
        {
            System.out.print(mst[i]+" ");
            mincost += mst[i].value;
        }
        System.out.println(",最小代价为"+mincost);
    }

(2))Kruskal算法
在这里插入图片描述
在这里插入图片描述

Kruskal算法实现

在这里插入代码片
 // edges为排序顺序表,从小到达存储各边,最小生成树的边存储在mst数组中
    public void minSpanTree(SortedSeqList <Triple> edges, Triple[] mst )
    {
        int[] parent=new int[vertexCount()]; //parent表示顶点的父母,parent[i]=-1表示顶点i没有父母
        int i, j, vp1, vp2;
        for(i=0;i<vertexCount();i++) //初始化parent数组,每个顶点各自构成一个连通分量
            parent[i]=-1;
        i=0;j=0;
        while(i<edges.size()&&j<vertexCount()-1) //依次选取权值较小的边
        { vp1=find(parent,edges[i].row); //查找顶点的父母
            vp2=find(parent,edges[i].column);
            if (vp1!=vp2) //选中的边两个顶点位于不同的连通分量
            { parent[vp2]=vp1; //合并两个连通分量
                mst[j]=edges.element[i];
                j++;
            }
            i++;
        }
    }
    //查找顶点的父母,即顶点所在的集合
    public static int find(int[] parent, int v)
    {
        int t;
        t=v;
        while(parent[t]>=0)
            t=parent[t];
        return t;
    }

7 图最短路径

设G=(V,E)是一个带权图,若G中从顶点vi到vj的一条路径(vi,⋯,vj),其路径长度dij是所有从vi到vj路径长度的最小值,则(vi,⋯,vj)是从vi到vj的最短路径,vi称为源点,vj称为终点。
最短路径算法
(1)求单源最短路径——Dijkstra算法
Dijkstra算法描述:
设S是已求得最短路径的顶点集合,初始S={vi},V-S是剩余顶点集合,算法重复执行以下操作:
• 从V-S中选取一个距离vi最短的顶点vj ,把vj加入到S中;
• 判断vi到V-S中顶点vk的距离dik,经过顶点vj是否比原来更短,若更短则
修改dik=dij+wjk。
在这里插入图片描述
算法实现
算法使用三个数组来实现:
• s数组表示集合S,若s[i]=1,则顶点vi ∈ 𝑆,否则vi∈ 𝑉 − 𝑆。
• dist数组保存最短路径长度。
• path数组保存最短路径上该顶点的前驱。
在这里插入图片描述
在这里插入图片描述
代码实现

 public void shortestPath(int i) //求带权图中顶点vi的单源最短路径,Dijkstra算法
    {
        int n = this.vertexCount(); //图的顶点数
        boolean[] vset = new boolean[n]; //已求出最短路径的顶点集合,初值全为false
        vset[i] = true; //标记源点vi在集合S中。若i越界,Java抛出序号越界异常
        int[] dist = new int[n]; //最短路径长度
        int[] path = new int[n]; //最短路径的终点的前一个顶点
        for (int j = 0; j < n; j++) //初始化dist和path数组
        {
            dist[j] = this.weight(i, j);
            path[j] = (j != i && dist[j] < MAX_WEIGHT) ? i : -1;
        }
        for (int j = (i + 1) % n; j != i; j = (j + 1) % n) //寻找从vi到vj的最短路径,vj在V-S集合中
        {
            int mindist = MAX_WEIGHT, min = 0; //求路径长度最小值及其下标
            for (int k = 0; k < n; k++)
                if (!vset[k] && dist[k] < mindist) {
                    mindist = dist[k]; //路径长度最小值
                    min = k; //路径长度最小值下标
                }
            if (mindist == MAX_WEIGHT) //若没有其他最短路径则算法结束; 此语句对非连通图必需
                break;
            vset[min] = true; //确定一条最短路径的终点min并入集合S
            for (int k = 0; k < n; k++) //调整从vi到V-S中其他顶点的最短路径及长度
                if (!vset[k] && this.weight(min, k) < MAX_WEIGHT && dist[min] + this.weight(min, k) < dist[k]) {
                    dist[k] = dist[min] + this.weight(min, k); //用更短路径替换
                    path[k] = min; //最短路径经过min顶点
                }
        }
        System.out.print(this.getVertex(i) + "的单源最短路径:");
        for (int j = 0; j < n; j++) //输出顶点vi的单源最短路径
            if (j != i) {
                SinglyList<T> pathlink = new SinglyList<T>(); //路径单链表,记录最短路径经过的各顶点,用于反序
                pathlink.insert(0, this.getVertex(j)); //单链表插入最短路径终点vj
                for (int k = path[j]; k != i && k != j && k != -1; k = path[k])
                    pathlink.insert(0, this.getVertex(k)); //单链表头插入经过的顶点,反序
                pathlink.insert(0, this.getVertex(i)); //单链表插入最短路径起点vi
                System.out.print(pathlink.toString() + "长度" + (dist[j] == MAX_WEIGHT ? "∞" : dist[j]) + ",");
            }
        System.out.println()
    }

Dijkstra算法的时间复杂度为𝑂(𝑛2)

(2)求每对顶点间的最短路径——Floyd算法
在这里插入图片描述
Floyd算法过程描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Floyd算法实现

 //AbstractGraph<T>类声明以下shortestPath()成员方法,对于邻接矩阵或邻接表表示的图,都可以调用执行
    public void shortestPath() //求带权图每对顶点间的最短路径及长度,Floyd算法
    {
        int n=this.vertexCount(); //图的顶点数
        Matrix path=new Matrix(n), dist=new Matrix(n); //最短路径及长度矩阵,初值为0
        for (int i=0; i<n; i++) //初始化dist、path矩阵
            for (int j=0; j<n; j++)
            { int w=this.weight(i,j);
                dist.set(i,j,w); //dist初值是图的邻接矩阵
                path.set(i,j, (i!=j && w<MAX_WEIGHT ? i : -1));
            }
        for (int k=0; k<n; k++) //以vk作为其他路径的中间顶点
            for (int i=0; i<n; i++) //测试每对从vi到vj路径长度是否更短
                if (i!=k)
                    for (int j=0; j<n; j++)
                        if (j!=k && j!=i && dist.get(i,j) > dist.get(i,k)+dist.get(k,j)) //若更短,则替换
                        {
                            dist.set(i, j, dist.get(i,k)+dist.get(k,j));
                            path.set(i, j, path.get(k,j));
                        }
        System.out.println("\n每对顶点间的最短路径如下:");
        for (int i=0; i<n; i++)
        {
            for (int j=0; j<n; j++)
                if (i!=j)
                    System.out.print(toPath(path,i,j)+"长度"+(dist.get(i,j)==MAX_WEIGHT ? "∞" : dist.get(i,j))+",");
            System.out.println();
        }
    }

7 拓扑排序

在这里插入图片描述
在这里插入图片描述
拓扑排序的基本思想:
(1)从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它;
(2)从网中删去该顶点,并且删去从该顶点发出的全部有向边;
(3)重复上述两步,直到剩余的网中不存在没有前驱的顶点为止
在这里插入图片描述
在这里插入图片描述

8.关键路径

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一个活动ai的最晚开始时间l(i)和其最早开始时间e(i)的差额d(i)=l(i)-e(i)是该活动完成的时间余量。它是在不增加完成整个工程所需的总时间的情况下,活动可以拖延的时间。当一活动的时间余量为零时,说明该活动必须如期完成,否则就会拖延完成整个工程的进度。所以称l(i)-e(i)=0,即l(i)=e(i)的活动ai是关键活动。
求关键路径的过程如下:
(1)求AOE网中所有事件的最早发生时间ve()。
(2)求AOE网中所有事件的最迟发生时间vl () 。
(3)求AOE网中所有活动的最早发生时间e () 。
(4)求AOE网中所有活动的最迟发生时间l () 。
(5)计算AOE网中所有活动的最晚时间与最早时间的差d () 。
(6)找出所有d ()为0的活动构成关键路径。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

常见面试题之垃圾收回

1. 简述Java垃圾回收机制&#xff1f;&#xff08;GC是什么&#xff1f;为什么要GC&#xff1f;&#xff09; 为了让程序员更专注于代码的实现&#xff0c;而不用过多的考虑内存释放的问题&#xff0c;所以&#xff0c;在Java语言中&#xff0c;有了自动的垃圾回收机制&#x…

javaUDP数据报套接字编程

0.前言 对于UDP协议来说&#xff0c;具有无连接&#xff0c;面向数据报的特征&#xff0c;即每次都是没有建立连接&#xff0c;并且一次发送全部数 据报&#xff0c;一次接收全部的数据报。 java中使用UDP协议通信&#xff0c;主要基于 DatagramSocket 类来创建数据报套接字&a…

探索人工智能的奇妙世界:解密AI技术的未来发展

作为一名热爱技术的开发者&#xff0c;当谈到人工智能&#xff08;AI&#xff09;和焦虑商业化时&#xff0c;我总会面临一个困境&#xff1a;到底是愁眉苦脸&#xff0c;还是开怀大笑&#xff1f;让我带你走进这个有趣又争议的话题。 首先我们需要面对AI的自学能力。这些智能…

Django4.0+使用rest_framework_jwt的问题

问题描述 python版本&#xff1a;3.10 Django版本&#xff1a;4.1 djangorestframework-jwt版本&#xff1a;1.11.0 在写jwt认证功能时&#xff0c;发现run的时候会报以下错误 from django.utils.translation import ugettext as _ ImportError: cannot import name ugettext…

day69_Vue进阶

今日内容 零、 复习昨日 零、 复习昨日 nginx 静态服务器(动静分离)反向代理服务器(代理后端服务器)负载均衡异步 前端工程化 —> java代码工程 一、使用Vue-Cli搭建Vue项目 1.1 什么是vue-cli cli: Command Line 命令行工具&#xff0c;vue-cli就是vue的命令行工具&#xf…

ThreadPoolExecutor 线程池源码学习

ThreadPoolExecutor 线程池源码学习 1.阅读源码 1.ThreadPoolExecutor.execute public void execute(Runnable command) {if (command null)throw new NullPointerException();// ctl 高三位记录线程状态。低29位记录线程池中线程数int c ctl.get();//位运算获取工作线程数 …

wireshark抓包实践

目录 ifconfig ( network interfaces configuring )tcpdump 命令tcpdump&wireshark例子 ifconfig ( network interfaces configuring ) eth0表示网卡UP代表网卡开启状态RUNNING代表网卡的网线被接上mtu1500: MTU&#xff08;最大传输单元&#xff09;是指在网络中传输数据时…

【javaEE面试题(五)在JMM(Java Memory Model (Java 内存模型))下谈volatile的作用】

volatile的作用 JMM下volatile作用 volatile 能保证内存可见性 volatile 修饰的变量, 能够保证 “内存可见性”. 代码在写入 volatile 修饰的变量的时候 改变线程工作内存中volatile变量副本的值将改变后的副本的值从工作内存刷新到主内存 代码在读取 volatile 修饰的变量的时…

B067-基础环境-抽取Basegit

目录 抽取base抽取domain和querymapper接口抽取service抽取 Git优点&#xff1a;Git安装及操作Git Bash命令行操作图形化客户端TortoiseGit操作Git集成Idea操作idea会把workspace作为本地仓库gitee操作idea解决代码冲突 抽取base 抽取domain和query domain&#xff1a;所有实体…

Nodejs 依赖包的存放路径设置(按其他博客修改路径后,安装路径仍在C盘的解决办法)

Nodejs 依赖包的存放路径设置 使用命令npm root -g 查看依赖包的安装位置 默认依赖包的安装位置是在C盘。为了防止C盘存太多东西&#xff0c;我这里已经将安装位置改到了D盘&#xff0c;下面就记录下修改的步骤。 1. 创建新的依赖包安装目录 在 nodejs 的安装目录下创建两个新…

8年资深测试总结,性能测试+性能优化(详细)进军高级测试...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能优化常见概念…

LabVIEW FPGA利用响应式数字电子板快速开发空间应用程序

LabVIEW FPGA利用响应式数字电子板快速开发空间应用程序 与传统的基于文本的语言相比&#xff0c;LabVIEW的编程和设计已被证明可以缩短开发时间。各种研究表明&#xff0c;生产率的提高在3到10倍之间。LabVIEW通过图形语言、集成开发环境和多个编译器的组合来实现这一点。 图…

qt对话框

完善文本编辑器 #include "second.h" #include "ui_second.h"second::second(QWidget *parent) :QWidget(parent),ui(new Ui::second) {ui->setupUi(this);this->setWindowTitle("聊天室界面");//设置标题this->setWindowIcon(QIcon(&…

边缘检测

目录 1、边缘检测原理 2、Sobel算子边缘检测 3、Scharr算子边缘检测​编辑 4、算子生成函数 5、Scharr、Sobel的使用 6、Laplacian算子边缘检测 7、Canny算子边缘检测 8、Laplacian、Canny的使用 1、边缘检测原理 2、Sobel算子边缘检测 3、Scharr算子边缘检测 4、算子生成函…

MySQL数据库 - 表的操作

目录 一、创建表 二、创建表案例 1、显示当前使用的数据库名 2、创建表 2.1 MyISAM存储引擎表 2.2 InnoDB存储引擎表 三、查看表结构 四、修改表 1、新增列 2、修改列类型 3、修改列名 4、修改表名 5、删除列 五、删除表 表的操作至少会涉及如下两类SQL语句&…

adb日常使用命令

重启电脑adb服务 adb start-server和adb kill-server mac中uiautoviewer的位置 android-sdk→tools→bin→uiautomatorviewer.bat adb查看本机abi类型 adb shell getprop ro.product.cpu.abi github 比较好的adb教程&#xff1a; https://github.com/mzlogin/awesome-adb a…

[VUE学习]权限管理系统前端vue实现9-动态路由,动态标签页,动态面包屑

1.动态路由 1.因为我们左侧权限菜单是根据不同用户显示不一样的 所以我们需要使用动态路由 来动态生成右侧路由信息 在总体布局页面添加router <router-view> 是 Vue Router 提供的组件&#xff0c;用于动态展示匹配到的路由组件内容。通过在合适的位置放置 <router-v…

将word中超链接的字体颜色更换成白色

文章目录 1、问题描述2、解决方法&#xff08;两种&#xff09;2.1 临时修改2.2 永久修改 1、问题描述 超链接是蓝色&#xff0c;需要将其换成正常颜色的字体 2、解决方法&#xff08;两种&#xff09; 2.1 临时修改 直接选中该字体&#xff0c;从字体的颜色那里选主题颜色…

zabbix安装监控客户端应用

添加 zabbix 客户端主机 服务端和客户端都配置时间同步 服务端和客户端都设置 hosts 解析 设置 zabbix 的下载源&#xff0c;安装 zabbix-agent2 在服务端验证 zabbix-agent2 的连通性 ​编辑 在 Web 页面中添加 agent 主机 自定义监控内容 在客户端创建自定义 key 1.明确…

XSS学习

目录 什么是XSS 概念 理解 XSS分类 存储型XSS 反射型XSS 原理 攻击过程 DOM型 攻击过程 DOM行XSS与反射型XSS区别 存储型XSS与反射型XSS区别 DVWA实验 反射型XSS low等级 JavaScript弹窗函数 攻击思路 攻击者web设计 medium等级 high等级 impissible等级 …