C++邻接表与有向图
参照《算法》实现了C++邻接表表示的有向图,实现了书中有向图的各种算法,包括判断有向图可达性,寻找有向图中的环,顶点的先序、后序和逆后序排列以及Kosaraju算法寻找强连通域。
具体算法不再记录,直接参考《算法》有向图一章,C++代码及注释如下:
#include<iostream>
#include<queue>
#include<algorithm>
#include<map>
#include<stack>
#include<vector>
using namespace std;
/*
char
顶点 中间节点
VNode ENode
0 | A --> 2(C) 1(B)
1 | B --> 4(E) 3(D) 0(A)
2 | C --> 6(G) 5(F) 0(A)
3 | D --> 7(H) 1(B)
4 | E --> 7(H) 1(B)
5 | F --> 6(G) 2(C)
6 | G --> 5(F) 2(C)
7 | H --> 4(E) 3(D)
int
顶点 中间节点
VNode ENode
0 | 0 --> 2 1
1 | 1 --> 4 3 0
2 | 2 --> 6 5 0
3 | 3 --> 7 1
4 | 4 --> 7 1
5 | 5 --> 6 2
6 | 6 --> 5 2
7 | 7 --> 4 3
*/
const int MAX = 20;
struct ENode //邻接表的中间节点
{
int adjvex; //对应索引
ENode* next;
};
typedef struct VNode //邻接表顶点
{
int vertex; //值
ENode* firstarc; //指向第一个中间节点
}AdjList[MAX];
class ALGraph //图
{
private:
AdjList adjList; //邻接表数组
AdjList reverse_adjList; //反向邻接表数组
int vexNum; //节点数量
int arcNum; //连边数量
bool visited[MAX]; //标记被访问
bool onStack[MAX]; //标记递归调用的栈上的所有顶点
int edgeTo[MAX]; //记录环中的点
vector<stack<int>> cycles; //环
queue<int> pre; //所有点的前序排列
queue<int> post; //所有点的后序排列
stack<int> reversePost; //所有点的逆后序排列
vector<int> SConnectedField; //强连通域
vector<vector<int>> SConnectedFileds; //强连通域集合
public:
void CreateGraph(); //创建图
void PrintGraph(); //打印图
void DirectedReach(int v); //有向图的可达性
void reachDFS(int v); //有向图可达性DFS
void DirectedCycle(); //寻找图中有向环
void cycleDFS(int v); //判断环DFS
void PrintCycles(); //打印所有环
void Order(bool reverse); //顶点排序
void orderDFS(int v, bool reverse); //顶点排序的DFS
void PrintOrder(); //打印序列
void Kosaraju(); //Kosaraju算法寻找强连通域
void KosarajuDFS(int v); //Kosaraju算法DFS
void PrintConnectedFields(); //打印强连通域
};
void ALGraph::CreateGraph()
{
cout << "请输入图的顶点数:" << endl;
cin >> this->vexNum;
cout << "请输入图的弧数:" << endl;
cin >> this->arcNum;
cout << "请输入顶点信息:" << endl;
for (int i = 0; i<this->vexNum; i++) //构建顶点数组
{
cin >> this->adjList[i].vertex;
this->reverse_adjList[i].vertex = this->adjList[i].vertex;
this->adjList[i].firstarc = nullptr;
this->reverse_adjList[i].firstarc = nullptr;
}
cout << "请输入" << this->arcNum << "个弧的信息:" << endl;
for (int i = 0; i<this->arcNum; i++) //构建每条邻接表
{
int h1, h2;
cin >> h1 >> h2;
ENode* temp = new ENode(); //构建正向图
temp->adjvex = h2;
temp->next = this->adjList[h1].firstarc;
this->adjList[h1].firstarc = temp;
temp = new ENode(); //构建反向图
temp->adjvex = h1;
temp->next = this->reverse_adjList[h2].firstarc;
this->reverse_adjList[h2].firstarc = temp;
}
}
void ALGraph::PrintGraph()
{
cout << "---------正序图---------" << endl;
for (int i = 0; i<this->vexNum; i++)
{
cout << this->adjList[i].vertex << "--------->";
ENode* p = this->adjList[i].firstarc;
while (p)
{
cout << this->adjList[p->adjvex].vertex << " ";
p = p->next;
}
cout << endl;
}
cout << "---------逆序图---------" << endl;
for (int i = 0; i<this->vexNum; i++)
{
cout << this->reverse_adjList[i].vertex << "--------->";
ENode* p = this->reverse_adjList[i].firstarc;
while (p)
{
cout << this->reverse_adjList[p->adjvex].vertex << " ";
p = p->next;
}
cout << endl;
}
}
void ALGraph::DirectedReach(int v)
{
for (int i = 0; i<this->vexNum; i++)
{
visited[i] = false;
}
reachDFS(v);
}
void ALGraph::reachDFS(int v) //深度优先搜索判断可达的点
{
visited[v] = true;
cout << this->adjList[v].vertex << " ";
ENode* p = this->adjList[v].firstarc;
while (p)
{
if (!visited[p->adjvex])
{
reachDFS(p->adjvex);
}
p = p->next;
}
}
void ALGraph::DirectedCycle()
{
for (int i = 0; i<this->vexNum; i++)
{
onStack[i] = false;
edgeTo[i] = 0;
visited[i] = false;
}
for (int i = 0; i<this->vexNum; i++)
{
if (!visited[i])
cycleDFS(i);
}
}
void ALGraph::cycleDFS(int v) //深度优先搜索寻找环
{
onStack[v] = true;
visited[v] = true;
ENode* p = this->adjList[v].firstarc;
while (p)
{
if (!visited[p->adjvex])
{
edgeTo[p->adjvex] = v;
cycleDFS(p->adjvex);
}
else if (onStack[p->adjvex]) //下一个顶点在栈中,表示形成一个环
{
stack<int> cycle;
for (int x = v; x != p->adjvex; x = edgeTo[x]) //记录环的各个点
cycle.push(x);
cycle.push(p->adjvex);
cycle.push(v);
cycles.push_back(cycle);
}
onStack[v] = false;
p = p->next;
}
}
void ALGraph::PrintCycles()
{
vector<stack<int>>::iterator it = cycles.begin();
for (; it != cycles.end(); it++)
{
while (!(*it).empty())
{
cout << (*it).top() << " ";
(*it).pop();
}
cout << endl;
}
}
void ALGraph::Order(bool reverse)
{
for (int i = 0; i<this->vexNum; i++)
{
visited[i] = false;
}
for (int i = 0; i<this->vexNum; i++)
{
if (!visited[i])
orderDFS(i, reverse);
}
}
void ALGraph::orderDFS(int v, bool reverse)
{
pre.push(v); //前序排列,在递归之前加入队列
visited[v] = true;
ENode* p = nullptr;
if (!reverse)
p = this->adjList[v].firstarc;
else
p = this->reverse_adjList[v].firstarc;
while (p)
{
if (!visited[p->adjvex])
orderDFS(p->adjvex, reverse);
p = p->next;
}
post.push(v); //后序排列,在递归之后加入队列
reversePost.push(v); //逆后序排列,递归之后加入栈
}
void ALGraph::PrintOrder()
{
cout << "前序排列:" << endl;
while (!pre.empty())
{
cout << pre.front() << " ";
pre.pop();
}
cout << endl << "后序排列:" << endl;
while (!post.empty())
{
cout << post.front() << " ";
post.pop();
}
cout << endl << "逆后序排列:" << endl;
while (!reversePost.empty())
{
cout << reversePost.top() << " ";
reversePost.pop();
}
cout << endl;
}
void ALGraph::Kosaraju() //Kosaraju算法寻找强连通域
{
this->Order(true); //首先进行拓扑排序,获取反向图的逆反排序
for (int i = 0; i<this->vexNum; i++)
{
visited[i] = false;
}
while (!reversePost.empty()) //按照逆反排序对正向图进行DFS
{
int v = reversePost.top();
reversePost.pop();
if (!visited[v])
{
KosarajuDFS(v);
SConnectedFileds.push_back(SConnectedField); //加入一个连通域
SConnectedField.clear();
}
}
}
void ALGraph::KosarajuDFS(int v) //Kosaraju的DFS
{
visited[v] = true;
SConnectedField.push_back(v); //同一连通域插入
ENode* p = this->adjList[v].firstarc;
while (p)
{
if (!visited[p->adjvex])
KosarajuDFS(p->adjvex);
p = p->next;
}
}
void ALGraph::PrintConnectedFields()
{
for (int i = 0; i<SConnectedFileds.size(); i++)
{
for (int j = 0; j<SConnectedFileds[i].size(); j++)
{
cout << SConnectedFileds[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
int main()
{
ALGraph* grapth = new ALGraph();
grapth->CreateGraph();
grapth->PrintGraph();
grapth->Kosaraju();
grapth->PrintConnectedFields();
return 0;
}