1.概述
在日常生活中,对象与对象之间的关系可以用示意图来表示。图在各个领域的应用广泛,在计算机的应用领域如开关理论,逻辑设计,人工智能,形式语言,操作系统以及编译程序及信息检索内,图都起着重要的作用。下面看一下图的几个概念:
有向图:有向图是连线带有箭头的,具有方向性。在有向图中,<Vi,Vj>属于边E的集合,未必有<Vj,Vi>属于E。
无向图:无向图是不带有箭头的,无方向性。在无向图中,(Vi,Vj)属于边E的集合,必须有<Vj,Vi>属于边E.、
邻结点:在无向图中,(Vi,Vj)属于E,则称Vi与Vj为端点,并称它们互为邻结点。在有向图中,<Vi,Vj>属于E,称Vi为起始点,Vj为终止点。Vi与Vj互为邻结点。称此边是Vi的一条出弧,Vj的一条入弧。
顶点的度:无向图中顶点所连接边的数目称为顶点的度。有向图中顶点的度是出度与入度之和。
入度:有向图中,该顶点入边的数目。
出度:有向图中,该顶点出边的数目。
权值:图中每条边上标注的值称为权值。可以表示费用,距离等等。
2. 图的存储模式
(1)邻结矩阵-构成nxn的矩阵,这个矩阵中的元素非0即1,如果两个顶点之间有边相连接,就为1,否则为0。可以看出,无向图的邻结矩阵是一个对称阵,可以用上三角矩阵或下三角矩阵表示。
(2)邻结链表
邻结链表可以看成是对邻结矩阵的改进,当图中所含有的边数较少时,会出现大量的零元素,如果存储这些零元素,将耗费大量的存储空间。因此可以把邻结矩阵的n个行向量改成单链表来表示,将同一个顶点出发的边链接到同一个单链表中去。邻结表中有两类结点:顶点结点与弧结点。
顶点结点包括数据域和第一个弧结点指针,而每个弧结点包括数据域与下一个邻结弧结点指针。
邻结表有利于计算图中某个顶点的出度。而计算入度就需要用到逆邻结表。
(3)逆邻结表:当计算图的入度时,如果对于邻结表的结构得扫描整个邻结表。逆邻结表中存储的是结点的入度信息而不是出度信息。
(4)邻结多重表:在有向图的邻结表中,求顶点的入度信息比较方便,在逆邻结表中,求顶点的出度信息比较方便。而邻结多重表是把邻结表与逆邻结表结合在一起,从而兼有邻结表与逆邻结表的优点。
3.图的存储实例
上述的例子就是邻结表存储有向图。
4.图的存储以及出度与入度的JAVA实现
顶点结点类:
package Graph;
/**
* 图中有两类节点,顶点结点与弧节点
* 用邻结列表进行存储
* @author Administrator
*
*/
public class VexNode {
int Vexdata;//节点元素值
ArcNode firstarc;//邻结节点
int indegree;//节点的入度域
}
弧结点类:
package Graph;
public class ArcNode {
int Arcdata;
ArcNode next;
}
图的实现类:
package Graph;
/**
*
* 建立无向图 用一个数组来存储图节点
*
* @author Administrator
*
*/
public class graph {
int max;// 图中最大节点数
int n = 0;// 图中当前节点数
VexNode[] adjlist;//邻结列表数组
boolean visit[];/* 记录节点有没有被访问过 */
/**
* 利用邻结矩阵生成邻结表
*
* @param vex
* 顶点数组
* @param arc
* 边的一维排列
* @param n
* 顶点个数
*/
public graph(int vex[], int arc[][], int n) {
this.n = max = n;
visit = new boolean[n];
adjlist = new VexNode[n];
//首先建立n个顶点结点
for (int k = 0; k < n; k++) {
adjlist[k] = new VexNode();
adjlist[k].Vexdata = vex[k];
}
//建立相对应的弧结点
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (arc[i][j] == 1) {
ArcNode p = new ArcNode();
p.next = adjlist[vex[i]].firstarc;
adjlist[vex[i]].firstarc = p;
p.Arcdata = vex[j];
}
}
}
}
public graph(int num) {
max = num;
adjlist = new VexNode[num];
visit = new boolean[num];
}
/**
* 建立图节点
*/
public void createNode(int data) {
if (n < max) {
VexNode v = new VexNode();
v.Vexdata = data;
adjlist[n] = v;
n++;
}
}
/**
* 建立有向图的边,建立一条从data1到data2的弧
*
* @param data1
* @param data2
*/
public void createDirectionArc(int data1, int data2) {
ArcNode p = new ArcNode();
p.next = adjlist[data1].firstarc;
adjlist[data1].firstarc = p;
p.Arcdata = data2;
adjlist[data2].indegree++;
}
/**
* 建立无向图中的边 即建立邻节表
*/
public void createArc(int data1, int data2) {
ArcNode p1;
ArcNode p2;
p1 = new ArcNode();
p2 = new ArcNode();
p1.next = adjlist[data1].firstarc;
p2.next = adjlist[data2].firstarc;
adjlist[data1].firstarc = p1;
adjlist[data2].firstarc = p2;
p1.Arcdata = data2;
p2.Arcdata = data1;
}
/**
* 建立一个有向图逆邻结表
*
* @param data1
* @param data2
*/
public void createInverseArc(int data1, int data2) {
ArcNode p = new ArcNode();
p.next = adjlist[data2].firstarc;
adjlist[data2].firstarc = p;
p.Arcdata = data1;
}
/**
* 逆邻结表计算有向图中顶点的入度
*
* @param v0
*/
public void InVex(int v0) {
ArcNode p = adjlist[v0].firstarc;
int indegree = 0;
while (p != null) {
indegree++;
p = p.next;
}
System.out.println("顶点" + v0 + "的入度为:" + indegree);
}
/**
*
* 逆邻结表计算顶点的出度
*
* @param v0
*/
public void outVex(int v0) {
int m = 0;
for (int i = 0; i < n; i++) {
ArcNode p = adjlist[i].firstarc;
if (v0 != i) {
while (p != null) {
if (p.Arcdata == v0) {
m++;
break;
} else {
p = p.next;
}
}
}
}
System.out.println("节点" + v0 + "的出度为:" + m);
}
/**
*
* 邻结表计算有向图中顶点的入度
*
*/
public void IndVex(int v0) {
int m = 0;
for (int i = 0; i < n; i++) {
ArcNode p = adjlist[i].firstarc;
if (v0 != i) {
while (p != null) {
if (p.Arcdata == v0) {
m++;
break;
} else {
p = p.next;
}
}
}
}
System.out.println("节点" + v0 + "的入度为:" + m);
}
/**
* 邻结表计算顶点的出度
*
*/
public void odvex(int v0) {
ArcNode p = adjlist[v0].firstarc;
int m = 0;
while (p != null) {
m++;
p = p.next;
}
System.out.println("顶点" + v0 + "的出度为:" + m);
}
/**
*
* 无向图中求顶点的度
*/
public void degree(int v0) {
int m = 0;
ArcNode p = adjlist[v0].firstarc;
while (p != null)
{
m++;
p = p.next;
}
System.out.println("顶点" + v0 + "的度为:" + m);
}
}
关于图的存储式以及出度入度的基本操作就介绍到这了,下面的文章将介绍图的深度优先以及广度优先遍历和一些其它的实用算法。