有向图的所有顶点使用整数1~n表示,对于每一个顶点,都有若干条有向边从该顶点出发,到达其他顶点,这些有向边的终点称为该顶点的邻接顶点。对于每一个顶点,都可以使用一条链表来记录它的所有邻接顶点,这条链表称为邻接链表。而有向图的所有顶点构成一个数组,所以所有顶点对应的邻接链表也构成了一个链表类型的数组。
邻接链表描述的有向图和邻接矩阵描述的有向图大多数函数都大同小异,本文对函数逻辑不做详细描述。以下代码主要为邻接链表描述下的有向图判断边是否存在、插入边、删除边、广度优先搜索、深度优先搜索。
关于有向图的更多阐述,详见[C++]有向图(邻接数组描述)
代码
先构建一个简单的链表类以满足邻接链表的需求
template<class T>
struct chainNode//定义链表节点
{
T element;
chainNode<T>* next;
chainNode() {}
chainNode(const T& element)
{
this->element = element;
}
chainNode(const T& element, chainNode<T>* next)
{
this->element = element;
this->next = next;
}
};
template<class T>
class graphChain//定义适合有向图的链表
{
public:
chainNode<T>* firstNode;
int listSize;
graphChain()
{
firstNode = NULL;
listSize = 0;
}
int indexOf(const T& theElement)const//获取指定节点在链表中的坐标,若该节点不存在则返回-1
{
chainNode<T>* currentNode = firstNode;
int index = 0;
while (currentNode != NULL && currentNode->element != theElement)
{
currentNode = currentNode->next;
index++;
}
if (currentNode == NULL)
return -1;
else
return index;
}
void insert(const T& theElement)//为提高效率,所有插入都采用头插法
{
firstNode = new chainNode<T>(theElement, firstNode);
listSize++;
}
bool erase(const T& theElement)//删除某节点,返回值表示删除是否成功(删除节点是否存在)
{
chainNode<T>* deleteNode;
if (theElement == firstNode->element)//删除首项
{
deleteNode = firstNode;
firstNode = firstNode->next;
}
else
{
chainNode<T>* currentNode = firstNode;
while (currentNode->next != NULL && currentNode->next->element != theElement)
currentNode = currentNode->next;
if (currentNode->next == NULL)//没找到该节点则返回false
return false;
deleteNode = currentNode->next;
currentNode->next = currentNode->next->next;
}
listSize--;
delete deleteNode;
return true;//删除成功返回true
}
};
邻接链表描述的有向图类
template<class T>
struct edge//定义有向边
{
int vertex1;//起点
int vertex2;//终点
T weight;//权值,对于无权图可以令T=bool,则weight可以表示该有向边是否存在
edge(const int& v1, const int& v2, const T& w)
{
vertex1 = v1;
vertex2 = v2;
weight = w;
}
};
class linkedDigraph//邻接链表描述的有向图
{
protected:
int n;//顶点个数
int e;//边个数
graphChain<int>* aList;//邻接链表组
public:
linkedDigraph(int theNumber)
{
n = theNumber;
e = 0;
aList = new graphChain<int>[n + 1];
}
~linkedDigraph() { delete[] aList; }
bool existsEdge(const int& v1, const int& v2) const//判断两点之间的有向边是否存在
{
if (v1<1 || v2<1 || v1>n || v2>n || aList[v1].indexOf(v2)==-1)
return false;
else
return true;
}
void insertEdge(edge<bool>* theEdge)//插入有向边
{
int v1 = theEdge->vertex1;
int v2 = theEdge->vertex2;
if (v1<1 || v2<1 || v1>n || v2>n || v1 == v2)
{
cout << "该有向边无效" << endl;
return;
}
if (aList[v1].indexOf(v2) == -1)
{
aList[v1].insert(v2);
e++;
}
else
cout << "该有向边已存在" << endl;
}
void eraseEdge(const int& v1, const int& v2)//删除有向边
{
if (v1 >= 1 && v2 >= 1 && v1 <= n && v2 <= n)
{
bool flag = aList[v1].erase(v2);//删除有向边,并返回删除是否成功
if (flag)
e--;
else
cout << "该有向边不存在" << endl;
}
}
void bfs(int v, int reach[], int label)//广度优先搜索(队列)
{
queue<int> q;
reach[v] = label;
q.push(v);
while (!q.empty())
{
int w = q.front();
cout << w << " ";//输出搜索到的顶点
q.pop();
for (chainNode<int>* u = aList[w].firstNode; u != NULL; u = u->next)
{
int temp = u->element;
if (reach[temp] == 0)
{
q.push(temp);
reach[temp] = label;
}
}
}
}
void dfs(int v, int reach[], int label)//深度优先搜索(递归)
{
reach[v] = label;
cout << v << " ";//输出搜索到的顶点
for (chainNode<int>* u = aList[v].firstNode; u != NULL; u = u->next)
{
int temp = u->element;
if (reach[temp] == 0)
dfs(temp, reach, label);
}
}
};