图结构是一种由数据元素集合及元素间关系集合组成的非线性数据结构,在图结构中,任意两个元素之间都可能有某种关系,因而每个数据元素可以有多个前驱和后继元素。图是表示离散结构的有力工具
一丶图的定义与基本术语
1:图的定义
图是一种非线性数据结构,其数据元素之间的关系没有限制,任意两个元素之间都有可能有某种关系。数据元素用结点(node)表示,如果相关,就用一条边(edge)将相应的结点连接起来,这两个结点称为相邻节点。
这样图就可以定义为由结点集合及结点间的关系集合组成的一种数据结构。下图展示了几个图的示例,结点又称为顶点(vertex),结点之间的关系称为边,一个图G记为G=(V,E),其中V是结点的有限集合,E是边的有限集合
1)无向图和有向图
如果任一条边e(x,y)仅表示两个节点x,y之间的相邻关系,没有方向性,则称边e(x,y)是无向的;则图称为无向图,一般用圆括号将一对结点括起来表示无向边
如果一个图中,任一条边e<x,y>是两个结点x和y的有序偶对,表示从结点x到y的单向通路,有方向性,则称边e<x,y>是有向的,一般用尖括号将一对结点括起来表示有向边。x称为有向边起点,y称为有向图终点,有向图的边又称为弧
2)完全图、稀疏图、稠密图
一个有n个结点的无向图,可能的最大边数是n×(n-1)/2,而一个n个结点的有向图,最大的弧的数目为n×(n-1),如果一个图的边数达到相同结点集合构成的所有图的最大边数,则称该图为完全图
一个有n个结点的图,其边的数目远小于n²,则称为稀疏图,如果接近最大数,则称为稠密图
3)带权图
在图中除了用边表示两个结点之间得相邻关系外,有时还需要表示它们相关得强度信息。例如从一个结点到另一个结点得距离,花费得事件,所需的时间,诸如此类得信息可以通过在图中每一条边上加一个称为权(weight)的数值来表示,这样的图成为带权图或网络
2:结点与边的关系
1)相邻节点
顾名思义
2)度,入度,出度
无向图中,与一个结点相关联的边的数目称为结点的度,记作TD(v),度1的结点称为悬挂点
有向图中,以结点v为终点的有向边的数目称为该结点的入度,记作I,D以结点v为起点的有向边的数目称为该结点的出度,记作OD,所以,TD=ID+OD
3:子图与生成子图
G个G1,如果G1中的所有元素都在G中,则称G1为G的子图,任一图G都是自身的子图
如果G1是G的子图,且V1=V,则称图G1是G的生成子图
4:路径丶回路与连通性
1)路径和回路
路径,顾名思义,有向图的路径也是有向的,一条路径的长度就是该条路径上边的数目
在带权图中,路径的长度有时指的是加权路径长度,它定义为从起点到终点上各条边的权值之和
回路顾名思义
2)图的连通性
如果无向图两个结点之间有一条路径,则称结点是连通的,,如果图任意结点之间都是连通的,则称图为连通图
如果有向图中的某两个结点之间有一条从i-j的路径,也有从j-i的路径,则称这两个结点是强连通的,如果图中任意的结点之间都是强连通的,则称该有向图是强连通图
5:图的基本操作
Initializi:初始化,建立一个图实例并初始化它的结点集合和边的集合
AddNode/AddNodes:在图中设置,添加一个或若干个结点
Get/Set:访问。获取或设置图中的指定节点
Count:求图的结点个数
AddEdge/AddEdges:添加一条或多条边
Nodes/Edges:获取结点表或边表
Remove:删除结点或边
Contains/IndexOf:查找,在图中查找满足某种条件的结点
Traversal:遍历,按某种次序访问图中的所有结点
Copy:复制,复制一个图
二丶图的存储结构
图结构是结点和边的集合,图的存储结构要记录这两方面的信息,结点的集合可以用一个称为结点表的线性表来表示;图中的一条边表示某两个结点的邻接关系,图的边集可以用邻接矩阵或邻接表来表示,邻接矩阵是顺序存储结构,邻接表是一种链式存储结构。
1:图结构的邻接矩阵表示法
1)临界矩阵的定义
图结构的邻接矩阵用来表示图的边集,即结点间的相邻关系集合,设G=(V,E)是一个具有n个结点的图它的邻接矩阵是一个n阶方阵,其中的元素具有下列性质:
,或者是有向图
,或者是有向图
如果是1,则这两个结点之间有边,如果是0,则这两个结点直接没有边
邻接矩阵作为其全部元素的整体则表示图的边集
上图的无向图的邻接矩阵如下:
上图的有向图的邻接矩阵如下:
2)带权图的邻接矩阵
对于带权图,如果有权重,则矩阵位置书写权重,如果没有连通,则为∞,结点自身位置则为0
3)邻接矩阵与结点的度
根据邻接矩阵容易求得各个节点的度,无向图中某节点的度等于图的邻接矩阵第i行上各元素之和
有向图的某节点的出度等于矩阵第i行上各元素之和,结点j的入度等于第j列上各元素之和
4)图的顶点和邻接矩阵图类的定义
下面定义Vertex类表示图中的顶点,成员data存储顶点的数据,成员vertex作为顶点是否被访问过的标志
public class Vertex<T>
{
private T data;
public bool visited;
public Vertex()
{
data=default(T);
visited=false;
}
public Vertex(T data ,bool vistied)
{
this.data=data;
this.visited=visited;
}
public Vertex(T data)
{
this.data=data;
this.visited=false;
}
public T Data
{
get{return data;}
set{data=value;}
}
public bool Visited
{
get{return visited;}
set{visited=value;}
}
public void Show()
{
Console.Write("-"+this.data+"->");
}
public override string ToString()
{
return string.Format("-{0}->",this.data);
}
}
下面定义AdjacencyMatrixGraph类表示一个以邻接矩阵存储的具有n个结点的图。成员变量count表示图的结点个数,成员变量vertexList是一个线性表,保存图的结点集合。成员变量AdjMat是一个二维数组,用来存储图的邻接矩阵
public class AdjacencyMatrixGraph<T>
{
private int count=0;
private IList<Vertex<T>> vertexLIst;
private int[,] AdjMat; //二维数组存储图的邻接矩阵
...
}
上面的两个类Vertex和AdjacencyMatrixGrapgh都声明在命名空间DSA中。
5)邻接矩阵图的基本操作
①邻接矩阵图的初始化
使用带一个二维数组参数的构造方法创建图对象,存储指定的邻接矩阵,并设置一个空的结点表;
缺省的构造方法则分别构建一个空的邻接矩阵和空的结点表,算法如下:
using System.Security.Cryptography;
public AdjacencyMatrixGraph(int[,],adjmat)
{
int n=adjmat.GetLength(0);
AdjMat=new int[n,n];
Array.Copy(adjmat,AdjMat,n*n);
vertexList=new List<Vertex<T>>();
Count=n;
}
public AdjacencyMatrixGraph()
{
AdjMat= new int [MaxVertexCount,MaxVertexCount];
vertexList=new List<Vertex<T>>();
Count=0;
}
②返回和设置图的结点数
该操作告知或设置图的结点个数,以名为Count的属性来实现该功能,编码如下:
public int Count
{
get{return count;}
set{count=value;}
}
③获取和设置指定节点的值
从0开始索引参数i来指示图的第i个结点,以类的索引器形式来实现这个功能,编码如下:
public T this[int i]
{
get{
if(i>=0&&i<count)
return vertexList[i].Data;
else
throw new IndexOutOfRangeException("IndexOutOfRangeException in"+this.GetType());
}
set{
if(i>=0&&i<count)
vertexList[i].Data=value;
else
throw new IndexOutOfRangeException("IndexOutOfRangeException in"+this.GetType());
}
}
④为图设置一组结点
该操作将图的结点表vertexList设为参数nodes指定的表,编码如下:
public void Addnodes(IList<Vertex<T>>nodes)
{
vertexList=nodes;
count=vertexList.Count;
}
⑤查找具有特定值的元素
在图中查找具有特定值k的过程为:在图中按结点号顺序检查结点值是否等于k,相等则返回结点号,否则继续比对下一个结点,当比较了所有的结点仍未找到,则返回-1,表示查找不成功
public int IndexOf(T k)
{
int j=0;
while(j<count&&!k.Equals(vertexList[i].Data))
j++;
if(j>=0&&j<count)
return j;
else return -1;
}
2:图结构的邻接表表示法
1)用邻接表表示无向图
2)用邻接表表示有向图
3)定义图的结点类和邻接表图类
4)临界图表的基本操作
三丶图的遍历
图的遍历是指从图的一个结点出发,以某种次序访问图中的每个结点,并且每个结点只被访问一次,对于图的遍历有两种策略,一种是深度优先搜索(depth first serch)遍历和广度优先搜索(breadth first serch)遍历。它的遍历可以从任意结点开始
深度优先类似于二叉树的先根遍历,优先从一条路径向更远处访问图的其他结点。
图的广度优先类似于二叉树的层次遍历,优先考虑直接近邻的结点,逐渐向远处扩展
1:基于深度优先策略的遍历
1)基于深度优先策略的遍历算法描述
2)邻接矩阵图的深度优先遍历操作的算法实现
例:邻接矩阵图的深度优先算法测试
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
namespace graphtest
{
class AdjacencyMatrixGraphTest
{
static void Main(string[]atgs)
{
int[,] adjmat={{0,1,1,1},{1,0,1,0},{1,1,0,1},{1,0,1,0}};
Vertex<string>[]nodes=new Vertex<string>[4];
for(int i=0;i<4;i++)
{
nodes[i]=new Vertex<string>("Vertex"+(i+1));
}
AdjacencyMatrixGraph<string> g=new AdjacencyMatrixGraph<string>(adjmat);
g.AddNodes(nodes);
DepthFirstShowTest(g);
}
static void DepthFirstShowTest(AdjacencyMatrixGraph<string> g)
{
Console.WriteLine("深度优先遍历:");
for(int i=0;i<g.Count;i++)
{
g.DepthFirstShow(i);
Console.WriteLine();
g.ResetVisitFlag();
}
}
}
}
3)邻接表图的深度优先遍历算法的实现
例:邻接表图的深度优先遍历算法测试
2:基于广度优先策略的遍历
1)基于广度优先策略的遍历
2)临界矩阵图的广度优先遍历算法实现
3)邻接表图的广度优先遍历算法实现
四丶最小代价生成树
图可以看成是树和森林的推广,树和森林则分别是图的某种特例
1:树与森林与图的关系
树和森林都是特殊的图
2:图的生成树
1)生成树的定义
2)生成森林
3)带权图的生成树
3:图的最小代价生成树
1)Kuskal算法
2)Prim算法