6.2.2 邻接表
邻接表(Adjacency List)是图的一种顺序存储与链式存储相结合的存储结构,类似于与树的孩子链表表示法。顺序存储指的是图中的顶点信息用一个顶点数组来存储,一个顶点数组元素是一个顶点结点,顶点节点有两个域,一个是数据域,存放与结点相关的信息,一个是引用域firstAdj。顶点的邻接表示把所有邻接于某顶点的顶点构成一个表,它是采用链式存储结构。所以,我们说邻接表是图的一种顺序存储与链式存储相结合的存储结构。
上图的邻接表如下,可以看出每个邻接点代表一个弧。如果是无向图,可以看成是特殊的有向图来表示。
邻接表的C#代码实现:
using System;
namespace GraphObject
{
//Graph node class
public class GraphNode<T>
{
public T Value { get; set; }
public GraphNode(T value)
{
Value = value;
}
}
//Actions of graph
public interface IGraph<T>
{
int NodeNum { get; }
int EdageNum { get; }
int IsNode(GraphNode<T> node);
bool SetNode(GraphNode<T> node);
bool DelNode(GraphNode<T> node);
bool SetEdge(GraphNode<T> node1, GraphNode<T> node2, int value);
bool DelEdge(GraphNode<T> node1, GraphNode<T> node2);
bool IsEdge(GraphNode<T> node1, GraphNode<T> node2);
}
//邻接结点类
public class AdjListNode<T>
{
public int AdjVexIndex { get; set; }
public int Weight { get; set; }
public AdjListNode<T> Next { get; set; }
public AdjListNode(int vexIndex, int weight)
{
this.AdjVexIndex = vexIndex;
this.Weight = weight;
this.Next = null;
}
}
//邻接根结点类
public class VexListNode<T>
{
public GraphNode<T> Node { get; set; }
public AdjListNode<T> FirstAdj { get; set; }
public VexListNode(GraphNode<T> node)
{
Node = node;
FirstAdj = null;
}
}
public class GraphAdjList<T> : IGraph<T>
{
private VexListNode<T>[] vexList;
public int NodeNum { get; private set; }
public int EdageNum { get; private set; }
public GraphAdjList(int length)
{
vexList = new VexListNode<T>[length];
}
public int IsNode(GraphNode<T> node)
{
for (int i = 0; i < NodeNum; i++)
{
if (vexList[i].Node == node)
return i;
}
return -1;
}
public bool SetNode(GraphNode<T> node)
{
if (IsNode(node) != -1)
return false;
VexListNode<T> vex = new VexListNode<T>(node);
vexList[NodeNum] = vex;
NodeNum += 1;
return true;
}
public bool DelNode(GraphNode<T> node)
{
int vexIndex = IsNode(node);
if (vexIndex == -1)
return false;
AdjListNode<T> adjNode = vexList[vexIndex].FirstAdj;
if (adjNode != null)
EdageNum -= 1;
while (adjNode.Next != null)
{
adjNode = adjNode.Next;
EdageNum -= 1;
}
vexList[vexIndex] = vexList[NodeNum - 1];
vexList[NodeNum - 1] = null;
NodeNum -= 1;
for (int i = 0; i < NodeNum; i++)
{
VexListNode<T> vexNode = vexList[i];
adjNode = vexNode.FirstAdj;
if (adjNode != null)
{
if (adjNode.AdjVexIndex == vexIndex)
{
vexNode.FirstAdj = adjNode.Next;
EdageNum -= 1;
}
else if (adjNode.AdjVexIndex == NodeNum)
adjNode.AdjVexIndex = vexIndex;
}
while (adjNode != null && adjNode.Next != null)
{
if (adjNode.Next.AdjVexIndex == vexIndex)
{
adjNode.Next = adjNode.Next.Next;
EdageNum -= 1;
}
else
{
if (adjNode.Next.AdjVexIndex == NodeNum)
adjNode.Next.AdjVexIndex = vexIndex;
adjNode = adjNode.Next;
}
}
}
return true;
}
public bool SetEdge(GraphNode<T> node1, GraphNode<T> node2, int value)
{
int vexIndex1 = IsNode(node1);
int vexIndex2 = IsNode(node2);
if (vexIndex1 == -1 || vexIndex2 == -1)
return false;
if (IsEdge(node1, node2))
return false;
AdjListNode<T> adjNode = new AdjListNode<T>(vexIndex2, value);
adjNode.Next = vexList[vexIndex1].FirstAdj;
vexList[vexIndex1].FirstAdj = adjNode;
EdageNum += 1;
return true;
}
public bool DelEdge(GraphNode<T> node1, GraphNode<T> node2)
{
if (!IsEdge(node1, node2))
return false;
int vexIndex1 = IsNode(node1);
int vexIndex2 = IsNode(node2);
VexListNode<T> vexNode = vexList[vexIndex1];
AdjListNode<T> adj = vexNode.FirstAdj;
if (adj.AdjVexIndex == vexIndex2)
{
vexNode.FirstAdj = adj.Next;
EdageNum -= 1;
return true;
}
while (adj.Next != null)
{
if (adj.Next.AdjVexIndex == vexIndex2)
{
adj.Next = adj.Next.Next;
EdageNum -= 1;
return true;
}
else
adj = adj.Next;
}
return false;
}
public bool IsEdge(GraphNode<T> node1, GraphNode<T> node2)
{
int vexIndex1 = IsNode(node1);
int vexIndex2 = IsNode(node2);
if (vexIndex1 == -1 || vexIndex2 == -1)
return false;
VexListNode<T> vexNode = vexList[vexIndex1];
AdjListNode<T> adjNode = vexNode.FirstAdj;
if (adjNode == null)
return false;
if (adjNode.AdjVexIndex == vexIndex2)
return true;
while (adjNode.Next != null)
{
if (adjNode.Next.AdjVexIndex == vexIndex2)
return true;
else
adjNode = adjNode.Next;
}
return false;
}
public void PrintGraphAdjList()
{
for (int i = 0; i < NodeNum; i++)
{
VexListNode<T> vexNode = vexList[i];
AdjListNode<T> adjNode = vexNode.FirstAdj;
Console.Write(vexNode.Node.Value);
while (adjNode != null)
{
Console.Write(" --(");
Console.Write(adjNode.Weight);
Console.Write(")--> ");
vexNode = vexList[adjNode.AdjVexIndex];
Console.Write(vexNode.Node.Value);
adjNode = adjNode.Next;
}
Console.WriteLine();
}
}
}
}
验证代码:
using GraphObject;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
GraphAdjList<int> adjList = new GraphAdjList<int>(100);
//Inial graph object
GraphNode<int> node1 = new GraphNode<int>(1);
GraphNode<int> node2 = new GraphNode<int>(2);
GraphNode<int> node3 = new GraphNode<int>(3);
GraphNode<int> node4 = new GraphNode<int>(4);
GraphNode<int> node5 = new GraphNode<int>(5);
GraphNode<int> node6 = new GraphNode<int>(6);
GraphNode<int> node7 = new GraphNode<int>(7);
GraphNode<int> node8 = new GraphNode<int>(8);
adjList.SetNode(node1);
adjList.SetNode(node2);
adjList.SetNode(node3);
adjList.SetNode(node4);
adjList.SetNode(node5);
adjList.SetNode(node6);
adjList.SetNode(node7);
adjList.SetNode(node8);
adjList.SetEdge(node2, node1, 2);
adjList.SetEdge(node3, node1, 3);
adjList.SetEdge(node2, node3, 4);
adjList.SetEdge(node3, node4, 5);
adjList.SetEdge(node5, node6, 4);
adjList.SetEdge(node8, node6, 1);
Assert.AreEqual<int>(8, adjList.NodeNum);
Assert.AreEqual<int>(6, adjList.EdageNum);
//Scenario 1: Test add new node
GraphNode<int> node9 = new GraphNode<int>(9);
adjList.SetNode(node9);
adjList.SetEdge(node9, node5, 3);
adjList.SetEdge(node9, node8, 1);
adjList.SetEdge(node6, node9, 2);
Assert.AreEqual<int>(9, adjList.NodeNum);
Assert.AreEqual<int>(9, adjList.EdageNum);
adjList.PrintGraphAdjList();
//Scenario 2: Test remove node
adjList.DelNode(node8);
Assert.AreEqual<int>(8, adjList.NodeNum);
Assert.AreEqual<int>(7, adjList.EdageNum);
adjList.SetNode(node8);
adjList.SetEdge(node8, node6, 1);
adjList.SetEdge(node9, node8, 1);
Assert.AreEqual<int>(9, adjList.NodeNum);
Assert.AreEqual<int>(9, adjList.EdageNum);
//Scenario 3: Test add/remove new edage
adjList.DelEdge(node3, node4);
Assert.AreEqual<int>(9, adjList.NodeNum);
Assert.AreEqual<int>(8, adjList.EdageNum);
adjList.SetEdge(node3, node4, 5);
Assert.AreEqual<int>(9, adjList.NodeNum);
Assert.AreEqual<int>(9, adjList.EdageNum);
//Scenario 4: Other medthods
Assert.IsTrue(adjList.IsNode(node4) >= 0, "node4 should be exist in matrix");
Assert.IsFalse(adjList.IsNode(new GraphNode<int>(10)) >= 0, "node4 should not be exist in matrix");
Assert.IsTrue(adjList.IsEdge(node3, node1), "edage should be exist in matrix");
Assert.IsFalse(adjList.IsEdge(node3, node2), "edage should not be exist in matrix");
}
}
}
上述代码的类结构图如下:
上述代码的类关系图如下: