不清楚的看大话数据结构这本书。
图结构比较复杂,任意两个顶点之间都可能存在联系,因此无法以数据元素在内存中的物理位置来表示元素之间的关系,也就是说,图不可能用简单的顺序存储结构来表示。多重链表虽能实现,也有相应的问题,具体看书。
存储结构有五种
- 邻接矩阵:边数少会浪费空间(一维数组+二维数组(或交错数组))(通用型)
- 邻接表:适合关注的重点是顶点的无向图(一维数组+一个链表))(对邻接矩阵的优化)
- 十字链表:适合有向图,同时关注入度和出度(一维数组+两个链表))(对邻接表的优化)
- 邻接多重表:适合关注的重点是边的无向图(一维数组+一个链表))(优化边的操作)
- 边集数组:适合对边依次进行处理的操作(两个一维数组)(优化边的操作)
邻接矩阵:边数少会浪费空间
图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。
/// <summary>
/// 邻接矩阵
/// </summary>
public class AdjacentMatrix<T>
{
#region 数据 :不需要的可以自行删除
private T[] vertexs; //顶点数组:数据
private int[,] edges; //边数组:权值
private int maxNumVertex; //最大顶点数
private int numVertex; //顶点数
private int numEdge; //边数
#endregion
/// <summary>
/// 创建无向网图的邻接矩阵表示
/// </summary>
/// <param name="vertexs">顶点数据</param>
/// <param name="edges">边数据:权值</param>
/// <param name="maxNumVertex">最大顶点数组容量</param>
public AdjacentMatrix(T[] vertexs, int[,] edges, int maxNumVertex)
{
maxNumVertex = maxNumVertex < vertexs.Length ? vertexs.Length : maxNumVertex;
this.vertexs = new T[maxNumVertex];
this.edges = new int[maxNumVertex, maxNumVertex];
this.maxNumVertex = maxNumVertex;
Array.Copy(vertexs, this.vertexs, vertexs.Length);
Array.Copy(edges, this.edges, edges.Length);
}
}
邻接表:适合无向图(关注的重点是顶点的无向图)
边数相对于顶点较少的图,邻接矩阵存在存储空间的极大浪费。
所以我们考虑对边或弧使用链式存储的方式避免空间浪费。
数组与链表相结合的存储方法称为邻接表
/// <summary>
/// 邻接表:一维数组 + 链表(数组和链表自行提供)
/// </summary>
public class AdjacencyList<T> where T : IEquatable<T>
{
/// <summary>
/// 边表结点
/// </summary>
public class EdgeNode
{
public int adjvex; //邻接点域,存储该顶点在顶点表中对应的下标
public int weight; //用于存储权值,对于非网图可以不需要(关键路径需要权值)
public EdgeNode next; //链域,指向下一个邻接点
}
/// <summary>
/// 顶点表结点
/// </summary>
public class VertexNode<T>
{
public int inDegree; //入度:拓扑排序用
public T data; //顶点域,存储顶点信息
public EdgeNode firstEdge; //边表头指针
}
private int numVertexes; //顶点数
private VertexNode<T>[] adjacencyArr; //顶点表
/// <summary>
/// 说明:没有按书上写,需要的数据自行提供,包括顶点数据和边表数据
/// </summary>
/// <param name="vertexData">顶点数据</param>
/// <param name="edgeData">边表数据</param>
public AdjacencyList(VertexNode<T>[] vertexData, EdgeNode[] edgeData)
{
numVertexes = vertexData.Length;
adjacencyArr = new VertexNode<T>[numVertexes];
for (int i = 0; i < numVertexes; i++) //建立顶点表
{
adjacencyArr[i].data = vertexData[i]; //顶点数据
adjacencyArr[i].firstEdge = edgeData[i]; //建立边表
}
}
}
十字链表:适合有向图
对于有向图来说,邻接表是有缺陷的,关心出度问题,想了解入度就必须要遍历整个图才能知道,反之,逆邻接表解决了入度却不了解出度的情况。
十字链表:把邻接表和逆邻接表结合起来
/// <summary>
/// 泛型十字链表:有向图的优化,同时关心入度和出度
/// </summary>
public class OrthogonalList<T>
{
/// <summary>
/// 边表结点
/// </summary>
public class EdgeNode
{
public int tailIndex; //弧起点在顶点表的下标
public int headIndex; //弧终点在顶点表的下标
public int weight; //用于存储权值,对于非网图可以不需要
public EdgeNode headLink; //入边表指针域,指向终点相同的下一条边
public EdgeNode tailLink; //出边表指针域,指向起点相同的下一条边
}
/// <summary>
/// 顶点表结点
/// </summary>
public class VertexNode<T>
{
public T data; //顶点域,存储顶点信息
public EdgeNode firstIn; //入边表头指针
public EdgeNode firstOut; //出边表头指针
}
public int numVertexes; //顶点数
public int numEdges; //边数
public VertexNode<T>[] orthList; //顶点表
/// <summary>
/// 说明:没有按书上写,需要的数据自行提供,包括顶点数据和边表数据
/// </summary>
/// <param name="vertexData">顶点数据</param>
/// <param name="tailEdgeData">出边表数据</param>
/// <param name="headEdgeData">入边表数据</param>
public OrthogonalList(T[] vertexData, EdgeNode[] tailEdgeData, EdgeNode[] headEdgeData)
{
numVertexes = vertexData.Length;
orthList = new VertexNode<T>[numVertexes];
for (int i = 0; i < numVertexes; i++) //建立顶点表
{
orthList[i].data = vertexData[i]; //顶点数据
orthList[i].firstIn = headEdgeData[i]; //建立入边表
orthList[i].firstOut = tailEdgeData[i]; //建立出边表
}
}
}
邻接多重表:对无向图的邻接表的优化(更关注边的操作)
/// <summary>
/// 邻接多重表:无向图的优化
/// </summary>
public class AdjacencyMultiList<T>
{
/// <summary>
/// 边表结点
/// </summary>
public class EdgeNode
{
public int iVex; //该顶点在顶点表中的下标
public EdgeNode iLink; //指向依附顶点iVex的下一条边
public int jVex; //该顶点在顶点表中的下标
public EdgeNode jLink; //指向依附顶点jVex的下一条边
public int weight; //用于存储权值,对于非网图可以不需要
}
/// <summary>
/// 顶点表结点
/// </summary>
public class VertexNode<T>
{
public T data; //顶点域,存储顶点信息
public EdgeNode firstEdge; //边表头指针
}
public int numVertexes; //顶点数
public VertexNode<T>[] adjacencyMultiList; //顶点表
/// <summary>
/// 说明:没有按书上写,需要的数据自行提供,包括顶点数据和边表数据
/// </summary>
/// <param name="datas">顶点数据</param>
/// <param name="edgeData">边数据</param>
public AdjacencyMultiList(T[] datas, EdgeNode[] edgeData)
{
numVertexes = datas.Length;
adjacencyMultiList = new VertexNode<T>[numVertexes];
for (int i = 0; i < numVertexes; i++) //建立顶点表
{
adjacencyMultiList[i].data = datas[i]; //顶点数据
adjacencyMultiList[i].firstEdge = edgeData[i]; //建立边表
}
}
}
边集数组:关注的是边的集合,适合对边依次进行处理的操作,不适合对顶点的相关操作
边集数组是有两个一维数组构成。一个是存储顶点的信息;另一个是存储边的信息,这个边数组每个数据元素由一条边的起点下标(begin)、终点下标 (end) 和权 (weight) 组成。
学习了克鲁斯卡尔(Kruskal)算法时在补充上
/// <summary>
/// 边集数组
/// </summary>
public class EdgeArrary<T>
{
public class Edge
{
public int begin; //起点下标
public int end; //终点下标
public int weight; //权值
}
private T[] vertexArr; //顶点数据
private Edge[] edgeArr; //边数据
/// <summary>
/// 创建编辑数组
/// </summary>
/// <param name="vertexData">顶点数据</param>
/// <param name="edgeData">边数据</param>
public EdgeArrary(T[] vertexData,Edge[] edgeData)
{
vertexArr = vertexData;
edgeArr = edgeData;
}
}