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;
}