1. 基本概念
- 圖分爲無向圖和有向圖。
- 與一個頂點相鄰接的頂點數叫做該頂點的度。在有向圖中,進入一個頂點的弧叫做該頂點的入度,從一個頂點發出的弧叫做該頂點的出度。
- 在無向圖中,若圖中任意一對頂點都是連通的,則稱此圖是連通圖。
- 在有向圖中,若任意一對頂點u和v間存在一條從u到v的路徑和從v到u的路徑,則稱此圖是強連通圖。
- 無向圖的一個極大連通子圖稱爲該圖的一個連通分量。
- 有向圖的一個極大強連通子圖稱爲該圖的一個強連通分量。
- 在圖的每條邊上加上一個數字作權,也稱代價,帶權的圖稱網。
關於圖的基本概念實在是太多了,大家可以找一本數據結構的書看看,這裏也推薦一篇博客可以看看圖(1)——圖的定義和基本概念
2. 圖的表示方式
圖基本上有二種表達方式,分別是鄰接表和鄰接矩陣。數據結構 學習筆記(七):圖(上):圖的表示方法(鄰接表,鄰接矩陣),遍歷(DFS,BFS)
這裏我們使用的是鄰接表方法。
3. 圖的存儲結構
class Graph {
private:
int V; // vertex num
std::list<int>* adj; // adjacency list
public:
Graph(int V);
~Graph();
void addEdge(int v, int w);
};
Graph::Graph(int V) {
this->V = V;
adj = new std::list<int> [V]; // init adjacency list
}
Graph::~Graph() {
delete [] adj; // need [] attention!
adj = nullptr;
}
void Graph::addEdge(int v, int w) {
adj[v].push_back(w);
}
int main()
{
int V(5);
Graph g(V); // create graph
std::set<int> edgeInput[V];
edgeInput[0].insert({1, 2});
edgeInput[1].insert({3, 4});
for (int i = 0; i < V; ++i) {
for (auto it = edgeInput[i].begin(); it != edgeInput[i].end(); ++it) {
g.addEdge(i, *it); // insert edge
}
}
return 0;
}
這裏的話我們使用鄰接表的方法,爲了方便我們這裏使用STL::list
結構,這樣話我們使用std::list<int>*
就可以實現了整個鄰接表的描述,這個比使用二級指針表述鄰接表要方便一點。
我們使用以下的有向無環圖作爲我們的測試例子:
4. 深度優先搜索遍歷
首先訪問圖中某一起始頂點0,然後由0出發,訪問與v鄰接且未被訪問的任一頂點1,再訪問與1鄰接且未被訪問的任一頂點3,……重複上述過程。當不能再繼續向下訪問時,依次退回到最近被訪問的頂點,若它還有鄰接頂點未被訪問過,則從該點開始繼續上述搜索過程,直到圖中所有頂點均被訪問過爲止。
DFS本質是一種遞歸調用,我們可以直接使用遞歸也可以使用棧來代替遞歸的過程,同時我們注意需要使用一個visited數組來保存我們的元素是否已經被遍歷過了。或者我們也可以使用std::set
進行代替。
void Graph::DFSUtil(int v, bool* visited) {
visited[v] = true;
std::cout << v << " ";
for (auto it = adj[v].begin(); it != adj[v].end(); ++it) {
if (!visited[*it]) {
DFSUtil(*it, visited);
}
}
}
void Graph::DFS() {
bool* visited = new bool[V];
for (int i = 0; i < V; ++i) {
visited[i] = false;
}
for (int j = 0; j < V; ++j) {
if (!visited[j]) {
DFSUtil(j, visited);
}
}
}
5. 廣度優先搜索遍歷
首先訪問起始頂點0,接着由0出發,依次訪問0的各個未訪問過的鄰接頂點1,2,…,,然後再依次訪問1,2,…,的所有未被訪問過的鄰接頂點;再從這些訪問過的頂點出發,再訪問它們所有未被訪問過的鄰接頂點……依次類推,直到圖中所有頂點都被訪問過爲止。
廣度優先搜索是一種分層的查找過程,每向前走一步可能訪問一批頂點,不像深度優先搜索那樣有往回退的情況,因此它不是一個遞歸的算法。爲了實現逐層的訪問,算法必須藉助一個輔助隊列,以記錄正在訪問的頂點的下一層頂點。
void Graph::BFSUtil(int v, bool* visited) {
std::queue<int> myqueue;
visited[v] = true;
myqueue.push(v);
while (!myqueue.empty()) {
v = myqueue.front();
std::cout << v << " ";
myqueue.pop();
for (auto it = adj[v].begin(); it != adj[v].end(); ++it) {
if (!visited[*it]) {
visited[*it] = true;
myqueue.push(*it);
}
}
}
}
void Graph::BFS() {
bool* visited = new bool[V];
for (int i = 0; i < V; ++i) {
visited[i] = false;
}
for (int j = 0; j < V; ++j) {
if (!visited[j]) {
BFSUtil(j, visited);
}
}
}
6. 完整代碼
#include <iostream>
#include <list>
#include <queue>
#include <set>
class Graph {
private:
int V; // vertex num
std::list<int>* adj; // adjacency list
void BFSUtil(int v, bool* visited);
void DFSUtil(int v, bool* visited);
public:
Graph(int V);
~Graph();
void addEdge(int v, int w);
void BFS(); // BFS
void DFS(); // DFS
};
Graph::Graph(int V) {
this->V = V;
adj = new std::list<int> [V]; // init adjacency list
}
Graph::~Graph() {
delete [] adj; // need [] attention!
adj = nullptr;
}
void Graph::addEdge(int v, int w) {
adj[v].push_back(w);
}
void Graph::BFSUtil(int v, bool* visited) {
std::queue<int> myqueue;
visited[v] = true;
myqueue.push(v);
while (!myqueue.empty()) {
v = myqueue.front();
std::cout << v << " ";
myqueue.pop();
for (auto it = adj[v].begin(); it != adj[v].end(); ++it) {
if (!visited[*it]) {
visited[*it] = true;
myqueue.push(*it);
}
}
}
}
void Graph::BFS() {
bool* visited = new bool[V];
for (int i = 0; i < V; ++i) {
visited[i] = false;
}
for (int j = 0; j < V; ++j) {
if (!visited[j]) {
BFSUtil(j, visited);
}
}
}
void Graph::DFSUtil(int v, bool* visited) {
visited[v] = true;
std::cout << v << " ";
for (auto it = adj[v].begin(); it != adj[v].end(); ++it) {
if (!visited[*it]) {
DFSUtil(*it, visited);
}
}
}
void Graph::DFS() {
bool* visited = new bool[V];
for (int i = 0; i < V; ++i) {
visited[i] = false;
}
for (int j = 0; j < V; ++j) {
if (!visited[j]) {
DFSUtil(j, visited);
}
}
}
int main()
{
int V(5);
Graph g(V); // create graph
std::set<int> edgeInput[V];
edgeInput[0].insert({1, 2});
edgeInput[1].insert({3, 4});
for (int i = 0; i < V; ++i) {
for (auto it = edgeInput[i].begin(); it != edgeInput[i].end(); ++it) {
g.addEdge(i, *it); // insert edge
}
}
std::cout << "DFS: " << std::endl;
g.DFS();
std::cout << "\nBFS: " << std::endl;
g.BFS();
return 0;
}
7. 參考文獻
這裏尤其感謝參考文獻2和3,主要也是參考這個代碼的,尤其是那種圖的數據結構還是比較方便的。