貪心演算法之用優先佇列解決最短路徑問題(Dijkstra演算法)
阿新 • • 發佈:2019-01-03
#include <iostream> #include <cstdio> #include <stack> #include <cstring> #include <queue> #include <cstdlib> using namespace std; //城市的節點數目的最大值 const int MAX_CITY_NUM = 100; //節點權值的最大值 const int MAX_POLICY = 1e7; /* 一定要記得如果初始化矩陣的話,肯定需要一個變數儲存長和寬的最大值, 如果看到權重的話,肯定是需要有個變數儲存最大值的權重 */ struct Node { //value是節點值,然後min_dist是源點到這個節點的最短路徑 int value, min_dist; //注意這裡前面不要加public Node(int value, int min_dist) { this->value = value; this->min_dist = min_dist; } //過載operator < bool operator < (const Node &node) const { return this->value > node.value; } }; class Dijkstra { public: //初始化工作 void init(); //dijkstra演算法 void dijkstra(); //顯示源點到其它頂點的經過的頂點 void showProcess(); //顯示源點到各個頂點的最小權重 void showMinPolicy(); private: //城市的節點數目和線段的個數和起始位置 int n, m, start; //初始化權重矩陣 int map[MAX_CITY_NUM][MAX_CITY_NUM]; //源點到各個頂點的最短具體陣列 int dist[MAX_CITY_NUM]; //下標表示當前節點值,然後值儲存為上個節點值 int p[MAX_CITY_NUM]; //是否加入集合S,如果在集合S裡面的話,值為true,否則在集合S-V裡面,值為false; bool flag[MAX_CITY_NUM]; }; //Dijkstra演算法 void Dijkstra::dijkstra() { priority_queue<Node> queue; Node node(start, 0); queue.push(node); //還是要記得初始化p[i], dist[i], flag[i] memset(flag, false, sizeof(flag)); for (int i = 1; i <= n; ++i) { dist[i] = MAX_POLICY; p[i] = -1; } dist[start] = 0; // flag[start] = true; while (!queue.empty()) { //取出佇列最小元素 Node node = queue.top(); queue.pop(); int value = node.value; //這裡防止重複 if (flag[value]) continue; //為了防止重複必須設定為true,然後上面判斷如果是true,就繼續, //相當於這裡為打開了這把鎖作標記,然後下次遇到這把鎖的時候看是否做了標記 //如果做了標記,就continue; flag[value] = true; for (int i = 1; i <= n; ++i) { if (!flag[i] && map[value][i] < MAX_POLICY) if (dist[i] > dist[value] + map[value][i]) { dist[i] = dist[value] + map[value][i]; queue.push(Node(i, dist[i])); p[i] = value; } } } } //打印出每個頂點的路徑,這裡值儲存了前一個節點的key //所以我們需要用到棧的特點,先進後出 void Dijkstra::showProcess() { int value; stack<int> stack; for (int i = 1; i <= n; ++i) { value = p[i]; std::cout << "源點"<< start << "到"<< i << "的路徑是"; while (value != -1) { stack.push(value); value = p[value]; } while (!stack.empty()) { //pop函式是出來棧,沒有返回值,先取出棧頂值,然後出棧 int node = stack.top(); stack.pop(); std::cout << node << "-"; } std::cout << i << "最短距離為" << dist[i] << std::endl; } } void Dijkstra::init() { //定點u到定點v的權重是w, 然後輸入的起始地點是start; int u, v, w; std::cout << "請輸入城市的節點個數" << std::endl; std::cin >> n; if (n <= 0) { std::cout << "輸入的城市節點個數因該大於0" << std::endl; return; } std::cout << "請輸入城市之間線路的個數" << std::endl; std::cin >> m; if (m <= 0) { std::cout << "輸入的城市之前的線路個數不能小於0" << std::endl; return; } //鄰接舉證的初始化,預設都為最大值,注意這裡下標都是從1開始 for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n; ++j) { map[i][j] = MAX_POLICY; } } std::cout << "請輸入城市頂點到城市頂點之前的權重" << std::endl; //這裡也可以使用while(--m),因為不涉及到用i for (int i = 0; i < m; ++i) { std::cin >> u >> v >> w; if (u > n || v > n) std::cout << "您輸入的定點有誤" << std::endl; //如果2次輸入一樣頂點,那麼取最小的 map[u][v] = min(map[u][v], w); } std::cout << "請輸入小明的位置" << std::endl; //請輸入起始的頂點 std::cin >> start; if (start < 0 || start > n) { std::cout << "輸入的起始城市定點有誤" << std::endl; return; } } void Dijkstra::showMinPolicy() { std::cout << "小明所在的位置 " << start << std::endl; for (int i = 1; i <= n; ++i) { std::cout << "小明(" << start << ")要去的位置是" << i; if (dist[i] == MAX_POLICY) std::cout << "無路可到" << std::endl; else std::cout << "最短距離為" << dist[i] << std::endl; } } int main() { Dijkstra dij; dij.init(); dij.dijkstra(); dij.showMinPolicy(); dij.showProcess(); return 0; }