java 数据结构 图

时间:2021-10-07 07:30:40

以下内容主要来自大话数据结构之中,部分内容参考互联网中其他前辈的博客,主要是在自己理解的基础上进行记录。

  图的定义

图是由顶点的有穷非空集合和顶点之间边的集合组成,通过表示为G(V,E),其中,G标示一个图,V是图G中顶点的集合,E是图G中边的集合。

无边图:若顶点Vi到Vj之间的边没有方向,则称这条边为无项边(Edge),用序偶对(Vi,Vj)标示。

有向图:若从顶点Vi到Vj的边是有方向的,则成这条边为有向边,也称为弧(Arc)。用有序对(Vi,Vj)标示,Vi称为弧尾,Vj称为弧头。如果任意两条边之间都是有向的,则称该图为有向图。

有向图G2中,G2=(V2,{E2}),顶点集合(A,B,C,D),弧集合E2={<A,D>,{B,A},<C,A>,<B,C>}.

权(Weight):有些图的边和弧有相关的数,这个数叫做权(Weight)。这些带权的图通常称为网(Network)。

    

  图的存储结构

  图的存储结构一般分为邻接矩阵和十字链表

  邻接矩阵:图的邻接矩阵存储方式是用两个数组来标示图。一个一位数组存储图顶点的信息,一个二维数组(称为邻接矩阵)存储图中边或者弧的信息。

  设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为:

java  数据结构  图

  

  十字链表:

  顶点表结点结构:

  java  数据结构  图

  firstin:表示入边表头指针,指向该顶点的入边表中第一个结点。

  firstout:表示出边表头指针,指向该顶点的出边表中的第一个结点。

  边表结点结构:

  java  数据结构  图

  tailvex:指弧起点在顶点表的下标。

  headvex:指弧终点在顶点表中的下标。

  headlink:指入边表指针域。

  taillink:指边表指针域。

  如果是网,还可以再增加一个weight域来存储权值。

  java  数据结构  图

  蓝线表示出度,红线表示入度

  十字链表的优点

  十字链表是把邻接表和逆邻接表整合在一起,这样既容易找到以Vi为尾的弧,也容易找到以Vi为头的弧,

  因而容易求的顶点的出度和入度。

  

  图的搜索:

  深度优先遍历:也有称为深度优先搜索,简称DFS。其实,就像是一棵树的前序遍历。它从图中某个结点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有      路径相通的顶点都被访问到。若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中的所有顶点都被访问到为止。

  基本实现思想:

  (1)访问顶点v;

  (2)从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;

  (3)重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。

  

  广度优先遍历:也称广度优先搜索,简称BFS。BFS算法是一个分层搜索的过程,和树的层序遍历算法类同,它也需要一个队列以保持遍历过的顶点顺序,以便按出队的顺序再去访问这些顶点的邻接顶点。

  基本实现思想:

  (1)顶点v入队列。

  (2)当队列非空时则继续执行,否则算法结束。

  (3)出队列取得队头顶点v;访问顶点v并标记顶点v已被访问。

  (4)查找顶点v的第一个邻接顶点col。

  (5)若v的邻接顶点col未被访问过的,则col入队列。

  (6)继续查找顶点v的另一个新的邻接顶点col,转到步骤(5)。

直到顶点v的所有未被访问过的邻接点处理完。转到步骤(2)。

  广度优先遍历图是以顶点v为起始点,由近至远,依次访问和v有路径相通而且路径长度为1,2,……的顶点。为了使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问,需设置队列存储访问的顶点。

  最小生成树

    我们把构造连通网的最小代价生成的树称为最小生成树,即权值最小的生成树。

  实现方式:

  1、普利姆算法(Prim)

    基本思想:假设G=(V,E)是连通的,TE是G上最小生成树中边的集合。算法从U={u0}(u0∈V)、TE={}开始。重复执行下列操作:

    在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(u0,v0)并入集合TE中,同时v0并入U,直到V=U为止。

   此时,TE中必有n-1条边,T=(V,TE)为G的最小生成树。

    Prim算法的核心:始终保持TE中的边集构成一棵生成树。

   注意:prim算法适合稠密图,其时间复杂度为O(n^2),其时间复杂度与边得数目无关,而kruskal算法的时间复杂度为O(eloge)跟边的数目有关,适合稀疏图。

    示例:

    

java  数据结构  图

  (1)图中有6个顶点v1-v6,每条边的边权值都在图上;在进行prim算法时,我先随意选择一个顶点作为起始点,当然我们一般选择v1作为起始点,好,现在我们设U集合为当前所找到最小生成树里面的   顶点,TE集合为所找到的边,现在状态如下:

      U={v1}; TE={};

  (2)现在查找一个顶点在U集合中,另一个顶点在V-U集合中的最小权值,如下图,在红线相交的线上找最小值。

  java  数据结构  图

  通过图中我们可以看到边v1-v3的权值最小为1,那么将v3加入到U集合,(v1,v3)加入到TE,状态如下:

U={v1,v3}; TE={(v1,v3)};

(3)继续寻找,现在状态为U={v1,v3}; TE={(v1,v3)};在与红线相交的边上查找最小值。

  java  数据结构  图

  我们可以找到最小的权值为(v3,v6)=4,那么我们将v6加入到U集合,并将最小边加入到TE集合,那么加入后状态如下:

  U={v1,v3,v6}; TE={(v1,v3),(v3,v6)}; 如此循环一下直到找到所有顶点为止。

  (4)下图像我们展示了全部的查找过程:

  java  数据结构  图

    

  2、克鲁斯卡尔算法(Kruskal)

    

  假设连通网N=(V,{E})。则令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{}),图中每个顶点自成一个连通分量。在E中选择最小代价的边,若该边依附的顶点落在T中不同的连通分       量中,则将该边加入到T中,否则舍去此边而选择下一条代价最小的边,依次类推,直到T中所有顶点都在同一连通分量上为止。

  示例如下:

  java  数据结构  图

    图中先将每个顶点看作独立的子图,然后查找最小权值边,这条边是有限制条件的,边得两个顶点必须不在同一个图中,如上图,第一个图中找到最小权值边为(v1,v3),且满足限制条件,继续查找边              (v4,v6),(v2,v5),(v3,v6),当查找到最后一条边时,仅仅只有(v2,v3)满足限制条件,其他的如(v3,v4),(v1,v4)都在一个子图里面,不满足条件,至此已经找到最小生成树的

所有边。

  上述所有的具体代码实现:

  

 /**
* java数据结构无向图的实现
* 2016/4/29
*/
package cn.Link; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
public class Graph { final static int MAX = 65535; //两个定点之间没有路径时的长度
int verticts; //顶点数
int sides; //顶点为verticts的连通图的边数值
int[][] arc;//存储图的二维数组
String[] vex;
Graph(int verticts){
this.verticts = verticts;
this.sides = 0;
for(int i = verticts-1;i>0;i--){
this.sides +=i; //获得连通图的边数值
}
this.arc = new int[verticts][verticts];
this.vex = new String[verticts];
//初始化一个有向图,所有边设为最大权值(即不可能达到的值)
for(int i = 0; i < verticts;i++){
for(int j = 0; j < verticts;j++){
if(i!=j){
this.arc[i][j] = MAX;
}else{
this.arc[i][j]=0;
} }
}
} //假设有这样一个图
public void addGraph(){
//顶点数据
this.vex[0] = "beijing";
this.vex[1] = "shanghai";
this.vex[2] = "tianjing";
this.vex[3] = "chengdu";
this.vex[4] = "changsha";
this.vex[5] = "chongqing"; //边的权值
for(int i = 1; i < verticts;i++){
for(int j = 0; j < i;j++){
int n = (int)(Math.random()*100); //随机生成权值
if(n > 0){
this.arc[i][j]=this.arc[j][i] = n;
}else if(n == 0){
this.arc[i][j]=this.arc[j][i] = MAX;
} }
}
} //利用图的二维数组输出
public void printGraph(){
for(int i = 0; i < verticts;i++){
//输出第一行的名称
if(i == 0){
System.out.print("* ");
for(int x=0;x<verticts;x++){
System.out.print(this.vex[x]+" ");
}
System.out.println();
System.out.println(" ==============================================================");
}
//给每行前面输出地址
System.out.print(this.vex[i]+" ");
for(int j = 0; j < verticts;j++){
System.out.print(this.arc[i][j]+" ");
}
System.out.println();
System.out.println();
}
} //深度优先遍历输出
public void DFS(Graph G,int i,boolean[] visited){
int j;
visited[i] = true;
System.out.print(G.vex[i]+" "); //打印顶点的值
for(j = 0;j < G.verticts;j++){
if(G.arc[i][j]>0 && !visited[j]){
DFS(G,j,visited); //对访问的邻接顶点递归调用
}
} }
public void DFSTraverse(Graph G){
boolean[] visited = new boolean[G.verticts];
for(int i = 0;i < G.verticts;i++){
visited[i] = false; //初始状态所有顶点都是未访问过的状态
}
for(int i = 0;i < G.verticts;i++){
if(!visited[i])
DFS(G,i,visited); //对未访问过的顶点调用DFS 如果是连通图,则只会执行一次
}
} //广度优先遍历输出
public void BFS(Graph G){
int i, j;
Queue<Integer> Q = new LinkedList<Integer>();
boolean[] visited = new boolean[G.verticts];
for(i = 0;i < G.verticts;i++){
visited[i] = false;
} for(i = 0; i < G.verticts;i++){ //对每一个顶点都进行循环
if(!visited[i]){
visited[i] = true;
System.out.print("##"+G.vex[i]+" ");
Q.offer(i);
while(Q != null){
if(Q.peek() != null){
i = Q.poll(); //将队首元素赋值给i 然后出队列
}else{return ;}
for(j = 0;j < G.verticts;j++){
//判断其他顶点与当前顶点存在边但未被访问过
if(G.arc[i][j] > 0 && !visited[j]){
visited[j] = true;
System.out.print("##"+G.vex[j]+" ");
Q.offer(j);
}
}
}
}
}
} //得到最小生成树之普利姆(Prim)算法
public void MiniSpanTree_Prim(Graph G){
int min, i, j, k;
int [] adjvex = new int[G.verticts]; //保存相关顶点下表
int [] lowcost = new int[G.verticts]; //保存相关顶点间边的权值
lowcost[0] = 0; //初始化第一个权值为0 ,即vex[0]已经加入到生成树
adjvex[0] = 0; //初始化第一个顶点下标为0
for(i = 1;i < G.verticts;i++){
lowcost[i] = G.arc[0][i]; //将vex[0]顶点与之有边的权值存入数组
adjvex[i] = 0; //初始化都为vex[0]的下标
} for(i = 1;i < G.verticts;i++){
min = MAX; //初始化最小权值为MAX:65535
j = 1;
k = 0;
//循环所有顶点
while(j < G.verticts){
if(lowcost[j] != 0 && lowcost[j] < min){ //如果权值不为0,而且小于最小值
min = lowcost[j];
k = j;
}
j++;
} System.out.println("("+k+", "+adjvex[k]+")"+" 权长:"+G.arc[adjvex[k]][k]); //打印当前顶点边中权值最小的边
lowcost[k] = 0; //将当前顶点的权值设为0,表示此顶点已将完成任务
for(j = 1;j < G.verticts;j++){ //循环所有顶点
if(lowcost[j] != 0 && G.arc[k][j] < lowcost[j]){ //如果下标为k的顶点各边权值小于此前这些顶点未被加入生成权值
lowcost[j] = G.arc[k][j]; //将较小的权值存入lowcost
adjvex[j] = k; //将下标为k的顶点存入adjvex
}
}
}
} //得到最小生成树之克鲁斯卡尔(Kruskal)算法
//得到边集数组并按权由大到小排序 这一步是利用Edge类来实现的
//注意 此函书还有错误,有时候会输出6条边,尚待解决(MinispanTree_kruskal在寻找的过程中不可能形成环路,所以不可能多一条边)
//上述错误已经解决,有两个地方出来问题,第一:Edge的begin必须小于end,否则在Find函数中判断将出现错误,因为如果end小于begin的话,end有可能
//出现等于0的情况,第二:循环每一条边时,i应该小于G.sides;而我之前写成了i<G.verticts
public void MiniSpanTree_Kruskal(Graph G){
Edge edge = new Edge();
edge.Edge_1(G);
//edge.PrintEdge();
int i, n, m;
int num = 0; //记录找到了多少条边
int parent[] = new int[G.verticts]; //定义一个数组用来判断边与边是否形成回路
for( i = 0;i < G.verticts;i++){
parent[i] = 0; //初始化数组为-1
}
for(i = 0;i < G.sides;i++){ // 循环每一条边,15为顶点
n = Find(parent,edge.edge[i].begin);
m = Find(parent,edge.edge[i].end);
if(n != m ){ //n不等于m 说明边与边没有形成环路
parent[n] = m;//将此边的尾节点放入下标为起点的parent中
System.out.println("("+edge.edge[i].begin+","+edge.edge[i].end+")"+" 权长:"+edge.edge[i].weight);
num++;
//for(int j = 0;j < G.verticts;j++){
//System.out.print(" !!!!"+parent[j]); //初始化数组为0
//}
}
if(num >= G.verticts) break; //如果找到了(顶点数-1)条边,并且没有构成回路,就已经完成任务了,不用再找了, }
}
public int Find(int[] parent,int f){ //查找连线顶点的尾部下表
while(parent[f] > 0){
f = parent[f];
}
return f;
} //测试函数
public static void main(String[] args){
Graph graph = new Graph(6); //创建一个顶点个数为6的图
graph.addGraph();
System.out.println("将图以二维矩阵的方式输出");
graph.printGraph();
System.out.println("深度优先搜索结果");
graph.DFSTraverse(graph);
System.out.println();
System.out.print("广度优先搜索结果");
System.out.println();
graph.BFS(graph);
System.out.println();
System.out.println("最小生成树之普利姆算法Prim ");
graph.MiniSpanTree_Prim(graph);
System.out.println();
System.out.println("最小生成树之克鲁斯卡尔算法Kruskal ");
graph.MiniSpanTree_Kruskal(graph); }
} //Edge类 利用深度优先遍历得到树的所有路径以及这些路径的权值,并根据权值的大小进行从小到大排序
class Edge{
public int begin; //这两个顶点的开始顶点
public int end; //这两个顶点的结束顶点
public int weight; //两个顶点之间的权值
Edge edge[] = new Edge[15]; //edge数组 图的边数没有传入,计算最大值
Edge(){}
public void Edge_1(Graph G){
DFSTraverse_1(G); //得到edge数组
sortEdge(); //对dege进行排序
} public void SetEdge(int begin,int end,int weight){
this.begin = begin;
this.end = end;
this.weight = weight; }
int k=0; //用于数组赋值是计数
//利用深度优先遍历得到edge数组
public void DFS_1(Graph G,int i,boolean[] visited){
int j; visited[i] = true;
//System.out.print(G.arc[i][i+1]+" "); //打印顶点的值
for(j = 0;j < G.verticts;j++){
if(G.arc[i][j]>0 && !visited[j]){
//System.out.print(G.arc[i][j]+" ");
DFS_1(G,j,visited); //对访问的邻接顶点递归调用
}
if(G.arc[i][j] > 0 && i > j){
this.edge[this.k] = new Edge();
edge[this.k].SetEdge(j,i,G.arc[i][j]);
this.k++;
} } }
public void DFSTraverse_1(Graph G){
boolean[] visited = new boolean[G.verticts];
for(int i = 0;i < G.verticts;i++){
visited[i] = false; //初始状态所有顶点都是未访问过的状态
}
for(int i = 0;i < G.verticts;i++){
if(!visited[i])
DFS_1(G,i,visited); //对未访问过的顶点调用DFS 如果是连通图,则只会执行一次
}
}
//对得到的数组进行排序
public void sortEdge(){
Edge newEdge = new Edge();
newEdge.edge[0] = new Edge();
for(int i = 0;i < this.edge.length;i++){
for(int j = i;j < this.edge.length;j++){
if(this.edge[i].weight > this.edge[j].weight){
newEdge.edge[0] = this.edge[i];
this.edge[i] = this.edge[j];
this.edge[j] = newEdge.edge[0];
}
}
}
}
//输出Edge数组,用以测试Edge是否创建、赋值成功
public void PrintEdge(){
for(int i = 0; i < this.edge.length;i++){
System.out.println("数组"+i+": "+this.edge[i].begin+" "+this.edge[i].end+" "+this.edge[i].weight);
}
}
}

java 数据结构 图的更多相关文章

  1. java数据结构----图

    1.图:.在计算机程序设计中,图是最常用的数据结构之一.对于存储一般的数据问题,一般用不到图.但对于某些(特别是一些有趣的问题),图是必不可少的.图是一种与树有些相像的数据结构,从数学意义上来讲,树是 ...

  2. Java数据结构——图的DFS和BFS

    1.图的DFS: 即Breadth First Search,深度优先搜索是从起始顶点开始,递归访问其所有邻近节点,比如A节点是其第一个邻近节点,而B节点又是A的一个邻近节点,则DFS访问A节点后再访 ...

  3. Java数据结构——图的基本理论及简单实现

    1. 图的定义图(graph)是由一些点(vertex)和这些点之间的连线(edge)所组成的:其中,点通常被成为"顶点(vertex)",而点与点之间的连线则被成为"边 ...

  4. Java数据结构——图

    点 //类名:Vertex //属性: //方法: class Vertex{ public char label; //点的名称,如A public boolean wasVisited; publ ...

  5. Java数据结构和算法(四)赫夫曼树

    Java数据结构和算法(四)赫夫曼树 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 赫夫曼树又称为最优二叉树,赫夫曼树的一个 ...

  6. 数据结构--图 的JAVA实现&lpar;上&rpar;

    1,摘要: 本系列文章主要学习如何使用JAVA语言以邻接表的方式实现了数据结构---图(Graph),这是第一篇文章,学习如何用JAVA来表示图的顶点.从数据的表示方法来说,有二种表示图的方式:一种是 ...

  7. 数据结构--图 的JAVA实现&lpar;下&rpar;

    在上一篇文章中记录了如何实现图的邻接表.本文借助上一篇文章实现的邻接表来表示一个有向无环图. 1,概述 图的实现与邻接表的实现最大的不同就是,图的实现需要定义一个数据结构来存储所有的顶点以及能够对图进 ...

  8. Java数据结构之队列的实现以及队列的应用之----简单生产者消费者应用

    Java数据结构之---Queue队列 队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在 ...

  9. 【Java数据结构学习笔记之二】Java数据结构与算法之栈&lpar;Stack&rpar;实现

      本篇是java数据结构与算法的第2篇,从本篇开始我们将来了解栈的设计与实现,以下是本篇的相关知识点: 栈的抽象数据类型 顺序栈的设计与实现 链式栈的设计与实现 栈的应用 栈的抽象数据类型   栈是 ...

随机推荐

  1. 采用sqlserver的缺省配置,在生产环境经常碰到系统响应慢(甚至hung的情况)

    请重视并正确配置sqlserver实例及数据库的参数,一般化的配置推荐如下: 1.数据和日志文件的初始大小分别设置为10G和2G,均设置为按照固定200M大小增长,不限制最大值: 2.sever实例设 ...

  2. 数据可视化&lpar;3&rpar;--Google Charts

    Google Chart API 是谷歌提供的一项动态生成图表的服务.你可以随时自定义图表,以适应网站的外观和感觉.图表使用HTML5/SVG技术提供给iPhone手机, iPad和Android的跨 ...

  3. Android清除本地数据缓存代码案例

    Android清除本地数据缓存代码案例 直接上代码: /*  * 文 件 名:  DataCleanManager.java  * 描    述:  主要功能有清除内/外缓存,清除数据库,清除shar ...

  4. Android之旅:梦想、学习、坚持、自信、淡定

    前段时间参加了2012年度IT博客大赛,进了前十强,写了一篇获奖感言,不过还没正式在CSDN发表出来.眼看2012年就要结束了,刚好借这个机会将2012年度IT博客大十强获奖感言发表出来,也算是对20 ...

  5. windows2008无线网卡和&period;net3&period;5安装

    今天在联想T420S笔记本上安装windows2008标准版,安装完成后部分驱动软件不能安装,要求.net framework3.5,下载.net3.5安装时提示应该用角色管理器安装. 根据提示打开服 ...

  6. oracle ebs 12&period;20 安装成功其过程失败日记及总结&lpar;1&rpar;

    由于公司业务须要,须要安装oracle ebs进行 form 开发,所以就開始了痛苦oracle ebs安装之过程.刚開始是在vm中win2003 server 中安装ebs,,不知是我自已的水平太差 ...

  7. MyEclipse如何全局搜索

    1全局搜索的启动方式 CTRL+H 2全局搜索自己选择搜索方式 自己选择要搜索的东西,简单吧,里面还有很多好玩的东西需要你去发现,加油! [正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之 ...

  8. DX11 Without DirectX SDK--03 渲染一个立方体

    回到 DirectX11--使用Windows SDK来进行开发 一个立方体有8个顶点,然而绘制一个立方体需要画12个三角形,如果按照前面的方法绘制的话,则需要提供36个顶点,而且这里面的顶点数据会重 ...

  9. spring,springMVC中常用注解

    一,使用注解: 在spring的配置文件applicationContext.xml中,加入注解扫描.配置项就配置了对指定的包进行扫描,以实现依赖注入. <?xml version=" ...

  10. 浅析XSS与CSRF

    浅析XSS与CSRF 在 Web 安全方面,XSS 与 CSRF 可以说是老生常谈了. XSS XSS,即 cross site script,跨站脚本攻击,缩写原本为 CSS,但为了和层叠样式表(C ...