圖論演算法的實現:(Dijstra Bellman-Ford DFS BFS)
阿新 • • 發佈:2020-12-04
#ifndef HEAD_H_ #define HEAD_H__ #include <iostream> #include <array> #include <vector> #include <queue> #include <cassert> #include <string> #include <fstream> #include<numeric> #include<cmath> #include<algorithm> template <typename T> using grap = std::vector<std::vector<T>>; template <typename T> class Graph { protected: enum color { White, black, gray }; struct Node { //T data; struct Node* next; struct Node* parents; std::size_t index; //頂點編號 int color; //頂點得顏色,記錄訪問標誌 double distance; //距離指標記號 int full_time; Node(std::size_t i) : index(i) { next = nullptr; parents = nullptr; color = White; distance = 0; full_time = 0; //深度優先遍歷之用 } }; using node = struct Node; using pnode = struct Node*; std::size_t size; std::vector<pnode> Matrix; std::vector<std::vector<T>> Weight; //權重矩陣 void insert(pnode& phead, pnode pnew); pnode root; //廣度優先樹的根節點 std::vector<std::string>area; int find_index(std::string area_); //列印路徑 private: //bool success(); //檢驗鄰接表是否建立成 //自定義排序函式(氣泡排序規則) void Sort(std::vector<pnode>& Array); grap<T> graph; //鄰接矩陣 void Initialize_Graph(); //初始化圖 void DFS_VISIT(pnode vertex); //DFS輔助函式 void convert_to_list(); //將鄰接矩陣轉換為鄰接表的形式 void printMinDistance(std::size_t i,bool f); //Bellman-Ford演算法的部分 //建立邊的儲存的資料結構 struct edge { std::size_t from; std::size_t to; T weight; edge(std::size_t f, std::size_t t,T w) :from(f), to(t),weight(w) {} }; //使用鄰接表建立最小路徑樹 //case1 建立邊的儲存 std::vector<edge>E; void build_edge(); //路徑鬆弛函式(Bellman_ford演算法的鬆弛函式) bool RELAX(edge e); bool Bellman_Ford(std::size_t i); //狄杰特斯拉演算法的鬆弛函式 void RELAX(pnode& u, pnode& v); //演算法的具體步驟 void Dijsktra(std::size_t i); void print_path(std::size_t i, std::size_t j);//列印路徑的函式 public: Graph() = default; Graph(grap<T> g) : graph(g) { convert_to_list(); }; Graph(grap<T>g, grap<T>w) :graph(g),Weight(w) { convert_to_list(); }; Graph(std::string path); //過載一個從檔案讀入鄰接矩陣和權重矩陣的建構函式 Graph(std::string path1, std::string path2); void check_AToB(std::string area_); void print_Graph(); void BFS(std::size_t i,bool print); //廣度優先遍歷 void DFS(); //時間複雜度為\Theta(|V|+|E|) void MIN_RODD(std::size_t index,std::size_t mode,bool road); }; template <typename T> void Graph<T>::convert_to_list() { size = graph.size(); //初始化鄰接矩陣 for (std::size_t t = 0; t < size; t++) { Matrix.push_back(new Node(t)); //從0開始編號 } //根據鄰接矩陣構造鄰接表 for (std::size_t i = 0; i < size; i++) { for (std::size_t j = 0; j < size; j++) { if (graph[i][j] == 1) { //i-->j存在邊得話 pnode pnew = new Node(j); insert(Matrix[i], pnew); } } } //assert(success() == true); } template<typename T> void Graph<T>::Sort(std::vector<pnode>& Array) { for (size_t i = 0; i < Array.size(); i++) { for (size_t j = Array.size() - 1; j >= i + 1; j--) { if (Array[j]->distance >= Array[j - 1]->distance) { std::swap(Array[j], Array[j - 1]); } } } } template<typename T> void Graph<T>::Initialize_Graph() { //初始化鄰接表即可 for (auto s : Matrix) { pnode pcur = s; while (pcur != nullptr) { pcur->parents = nullptr; pcur->full_time = 0; pcur->distance = 0; pcur->color = White; pcur = pcur->next; } } } template <typename T> void Graph<T>::printMinDistance(std::size_t i,bool f) { if (f == false) { for (auto m : Matrix) { std::cout << m->index << "-->" << i << "min:" << m->distance << std::endl; } } else if (f == true) { //按照檔案中的區域列印 for (auto m : Matrix) { std::cout << area[m->index] << "-->" << area[i] << "min:" << m->distance << std::endl; } } } template<typename T> void Graph<T>::build_edge() { //根據鄰接表建立邊的儲存 for (size_t i = 0; i < graph.size(); i++) { for (size_t j = 0; j < graph[i].size(); j++) { if (graph[i][j] == 1) { auto e = edge(i, j, Weight[i][j]); E.push_back(e); } } } } template<typename T> bool Graph<T>::RELAX(edge e) { if (Matrix[e.to]->distance > (Matrix[e.from]->distance + e.weight)) { Matrix[e.to]->distance = (Matrix[e.from]->distance + e.weight); Matrix[e.to]->parents = Matrix[e.from]; return true; } return false; } template<typename T> void Graph<T>::RELAX(pnode& u, pnode& v) { if (Matrix[v->index]->distance > (Matrix[u->index]->distance + Weight[u->index][v->index])) { Matrix[v->index]->distance = (Matrix[u->index]->distance + Weight[u->index][v->index]); Matrix[v->index]->parents = Matrix[u->index]; } } template<typename T> void Graph<T>::Dijsktra(std::size_t i) { //初始化圖 for (size_t j = 0; j < Matrix.size(); j++) { if (j != i) { Matrix[j]->distance = INFINITY; Matrix[j]->parents = nullptr; } if (j == i) { Matrix[j]->distance = 0; Matrix[j]->parents = nullptr; } } std::vector<pnode>S;//swap auto f = [](pnode p1, pnode p2) { if (p1->distance >= p2->distance) { return true; } return false; }; std::vector<pnode>Matrix1 = Matrix; while (!Matrix1.empty()) { Sort(Matrix1); auto index = Matrix1.size() - 1; auto pcur1 = Matrix1[index]; auto pcur2 = Matrix1[index]->next; Matrix1.pop_back(); S.push_back(pcur1); while (pcur2!=nullptr) { RELAX(pcur1, pcur2); pcur2 = pcur2->next; } } } template<typename T> Graph<T>::Graph(std::string path1, std::string path2) { //讀入鄰接矩陣 std::string P = path1; std::ifstream FILE(P, std::ios::in); std::string line; int flag1 = 0; int flag2 = 0; std::vector<std::vector<int>>Matrix; while (std::getline(FILE, line)) { std::istringstream L(line); std::string n; if (flag1 == 0) { while (std::getline(L, n, ',')) { area.push_back(n);//加入地區 } flag1++; } else { flag2 = 0; std::vector<int>temp; while (std::getline(L, n, ',')) { if (flag2 != 0) { //讀入鄰接矩陣即可 temp.push_back(std::stoi(n)); } flag2++; } Matrix.push_back(temp); } } graph = Matrix; convert_to_list(); FILE.close();//關閉檔案 //讀入權重矩陣 std::ifstream FILE2(path2, std::ios::in); std::string line2; int flag3 = 0; std::vector<std::vector<int>>G; while (std::getline(FILE2, line2)) { std::vector<int>p; std::stringstream ss(line2); std::string n; flag3 = 0; while (std::getline(ss, n, ',')) { if (flag3 != 0) { p.push_back(std::stoi(n)); } flag3++; } G.push_back(p); } //開始填補空缺的部分 auto size = G.size(); for (std::size_t i = 0; i < G.size(); i++) { if (G[i].size() != size) { auto size2 = G[i].size(); for (size_t j = 0; j < size - size2; j++) { G[i].push_back(0); } } } Weight = G; } template<typename T> void Graph<T>::check_AToB(std::string area_) { int limit;//限制的最小路徑 auto index = find_index(area_); assert(index != -1); if (index != -1) { BFS(index,false); } //列印節點 std::cout << "Please enter a minimum limit path length:"; std::cin >> limit; std::vector<int>p; for (auto s:Matrix) { p.push_back(s->distance); } if ((std::accumulate(p.begin(), p.end(), 0)) >(limit * p.size())) { std::cout << "not exist" << std::endl; } else { std::cout << "exists" << std::endl; printMinDistance(index,true); } } template <typename T> void Graph<T>::DFS() { Initialize_Graph(); for (auto s : Matrix) { if (s->color == White) { DFS_VISIT(s); } } std::cout << std::endl; auto check = [](std::vector<pnode> Matrix) { auto len = 0; for (auto s : Matrix) { if (s->color == black) { len++; } } if (len == Matrix.size()) { return true; } else { return false; } }; assert(check(Matrix) == true); //檢驗DFS得正確性是否成立 } template<typename T> void Graph<T>::MIN_RODD(std::size_t index, std::size_t mode,bool road) { /* par1: index確定從哪一個頂點作為起始的頂點開始列印 par2: mode確定選擇哪一個演算法作為最短路徑演算法 par3: 是否列印最短路徑(之前必須初始化area容器) */ assert(mode == 1 || mode == 2);//mode=1呼叫FORD演算法 mode=2呼叫Dijstra演算法 if (mode == 1) { Bellman_Ford(index); } else if(mode == 2) { Dijsktra(index); } //列印路徑 if (road == true) { //printMinDistance(index, true); for (size_t i = 0; i < area.size(); i++) { if (i != index) { print_path(index,i); } } } } template<typename T> bool Graph<T>::Bellman_Ford(std::size_t i) { //初始化 build_edge(); for (size_t j = 0; j < Matrix.size(); j++) { if (j != i) { Matrix[j]->distance = INFINITY; Matrix[j]->parents = nullptr; } if (j == i) { Matrix[j]->distance = 0; Matrix[j]->parents = nullptr; } } //演算法的部分 bool update; for (size_t k = 1; k <Matrix.size(); k++) { for (std::size_t k2 = 0; k2 < E.size(); k2++) { //進行鬆弛 RELAX(E[k2]); } } //檢查演算法的正確性 for (size_t p = 0; p <E.size() ; p++) { auto index_u = E[i].from; auto index_v = E[i].to; if (Matrix[index_v]->distance > (Matrix[index_u]->distance + Weight[index_u][index_v])) { return false; } } return true; } template <typename T> void Graph<T>::DFS_VISIT(pnode vertex) { vertex->color = gray; Matrix[vertex->index]->color = gray; std::cout << vertex->index << "\t"; pnode pcur = vertex->next; while (pcur != nullptr) { if (pcur->color == White) { Matrix[pcur->index]->parents = vertex; DFS_VISIT(pcur); } } vertex->color = black; Matrix[vertex->index]->color = black; } template <typename T> void Graph<T>::BFS(std::size_t i,bool print) { Initialize_Graph(); std::size_t sum = Matrix.size(); //圖得總共得節點得個數有多少 pnode first = Matrix[i]; //選擇起始得節點 root = first; //設定廣度優先樹的根節點 first->color = gray; std::queue<pnode> Q; Q.push(first); //開始遍歷 std::cout << first->index << "\t"; std::size_t run_size = 1; while (!Q.empty()) { pnode e = Q.front(); Q.pop(); pnode pcur = e; //前驅 e = e->next; while (e != nullptr) { auto index = e->index; if (Matrix[index]->color == White) { Matrix[index]->color = gray; std::cout << Matrix[index]->index << "\t"; Matrix[index]->distance = Matrix[pcur->index]->distance + 1; Q.push(Matrix[index]); run_size++; } e = e->next; } } std::cout << std::endl; if (print == true) { printMinDistance(i,false); } assert(run_size == sum); } template<typename T> Graph<T>::Graph(std::string path) { std::string P = path; std::ifstream FILE(P, std::ios::in); std::string line; int flag1 = 0; int flag2 = 0; std::vector<std::vector<int>>Matrix; while (std::getline(FILE, line)) { std::istringstream L(line); std::string n; if (flag1 == 0) { while (std::getline(L, n, ',')) { area.push_back(n);//加入地區 } flag1++; } else { flag2 = 0; std::vector<int>temp; while (std::getline(L, n, ',')) { if (flag2 != 0) { //讀入鄰接矩陣即可 temp.push_back(std::stoi(n)); } flag2++; } Matrix.push_back(temp); } } graph = Matrix; convert_to_list(); } template <typename T> void Graph<T>::print_Graph() { for (auto s : Matrix) { while (s != nullptr) { if (s->next == nullptr) { std::cout << s->index; } else { std::cout << s->index << "->"; } s = s->next; } std::cout << std::endl; } std::cout << "-------------Adj-Matrix---------------" << std::endl; for (auto s : graph) { for (auto s1 : s) { std::cout << s1 << "\t"; } std::cout << std::endl; } std::cout << "---------------Weight-Matrix-------------" << std::endl; for (auto w : Weight) { for (auto w1 : w) { std::cout << w1 << "\t"; } std::cout << std::endl; } } template <typename T> void Graph<T>::insert(pnode &phead, pnode pnew) { assert(phead != nullptr); pnode pcur = phead; while (pcur->next != nullptr) { pcur = pcur->next; } pcur->next = pnew; } template<typename T> int Graph<T>::find_index(std::string area_) { for (int i = 0; i < area.size(); i++) { if (area[i] == area_) { return i; } } return -1; } template<typename T> void Graph<T>::print_path(std::size_t i, std::size_t j) { //i是源節點 j是目的節點 pnode pcur = Matrix[j]; //儲存到檔案 std::ofstream FILE("out.txt", std::ios::app); while (pcur!=Matrix[i]&&pcur!=nullptr) { //std::cout << pcur->index << "->"; std::cout << area[pcur->index] << "->"; FILE<< area[pcur->index] << "->"; pcur = pcur->parents; } /*for (auto s : Matrix) { std::cout << s->distance << std::endl; }*/ std::cout<<area[Matrix[i]->index]<< std::endl; FILE << area[Matrix[i]->index] << "\n"; FILE.close(); } #endif