Dijkstra算法


---------------------siwuxie095

   

   

   

   

   

   

   

   

Dijkstra 算法

   

   

這里介紹 Dijkstra 算法,它是一個應用最為廣泛的、名氣也是

最大的單源最短路徑算法

   

   

Dijkstra 算法有一定的局限性:它所處理的圖中不能有負權邊

   

「前提:圖中不能有負權邊」

   

換句話說,如果一張圖中,但凡有一條邊的權值是負值,那么

使用 Dijkstra 算法就可能得到錯誤的結果

   

   

不過,在實際生活中所解決的問題,大部分的圖是不存在負權

邊的

   

如:有一個路線圖,那么從一點到另外一點的距離肯定是一個

正數

   

所以,雖然 Dijkstra 算法有局限性,但是並不影響在實際問題

的解決中非常普遍的來使用它

   

   

   

   

   

看如下實例:

   

1)初始

   

   

   

左邊是一張連通帶權有向圖,右邊是起始頂點 0 到各個頂點的

當前最短距離的列表,起始頂點 0 到自身的距離是 0

   

   

   

   

2)將頂點 0 進行標識,並作為當前頂點

   

   

   

對當前頂點 0 的所有相鄰頂點依次進行松弛操作,同時更新列表

   

   

從列表的未標識頂點中找到當前最短距離最小的頂點,即 頂點 2

就可以說,起始頂點 0 到頂點 2 的最短路徑即 0 -> 2

   

因為:圖中沒有負權邊,即便存在從頂點 1 到頂點 2 的邊,也不

可能通過松弛操作使得從起始頂點 0 到頂點 2 的距離更小

   

   

圖中沒有負權邊保證了:對當前頂點的所有相鄰頂點依次進行松

弛操作后,只要能從列表的未標識頂點中找到當前最短距離最小

的頂點,就能確定起始頂點到該頂點的最短路徑

   

   

   

   

3)將頂點 2 進行標識,並作為當前頂點

   

   

   

   

   

4)對當前頂點 2 的相鄰頂點 1 進行松弛操作,同時更新列表

   

   

   

   

   

5)對當前頂點 2 的相鄰頂點 4 進行松弛操作,同時更新列表

   

   

   

   

   

6)對當前頂點 2 的相鄰頂點 3 進行松弛操作,同時更新列表

   

   

   

從列表的未標識頂點中找到當前最短距離最小的頂點,即 頂點 1

就可以說,起始頂點 0 到頂點 1 的最短路徑即 0 -> 2 -> 1

   

   

   

   

7)將頂點 1 進行標識,並作為當前頂點

   

   

   

   

   

8)對當前頂點 1 的相鄰頂點 4 進行松弛操作,同時更新列表

   

   

   

從列表的未標識頂點中找到當前最短距離最小的頂點,即 頂點 4

就可以說,起始頂點 0 到頂點 4 的最短路徑即 0 -> 2 -> 1 -> 4

   

   

   

   

9)將頂點 4 進行標識,並作為當前頂點

   

   

   

當前頂點 4 沒有相鄰頂點,不必進行松弛操作

   

從列表的未標識頂點中找到當前最短距離最小的頂點,即 頂點 3

就可以說,起始頂點 0 到頂點 3 的最短路徑即 0 -> 2 -> 3

   

   

   

   

10)將頂點 3 進行標識,並作為當前頂點

   

   

   

對當前頂點 3 的相鄰頂點 4 進行松弛操作,發現不能通過

松弛操作使得從起始頂點 0 到頂點 4 的路徑更短,所以保

持原有最短路徑不變

   

   

至此,列表中不存在未標識頂點,Dijkstra 算法結束,找

到了一棵以頂點 0 為根的最短路徑樹

   

   

   

   

   

Dijkstra 算法的過程總結

   

第一步:從起始頂點開始

   

第二步:對當前頂點進行標識

   

第三步:對當前頂點的所有相鄰頂點依次進行松弛操作

   

第四步:更新列表

   

第五步:從列表的未標識頂點中找到當前最短距離最小

       的頂點,作為新的當前頂點

   

第六步:重復第二步至第五步,直到列表中不存在未標

             識頂點

   

   

   

   

   

其實 Dijkstra 算法主要做兩件事情

   

1)從列表中找最值

   

2)更新列表

   

   

顯然,借助最小索引堆作為輔助數據結構,就可以非常

容易地實現這兩件事情

   

最后,Dijkstra 算法的時間復雜度:O(E*logV)

   

   

   

   

   

程序:

   

Edge.h:

   

#ifndef EDGE_H

#define EDGE_H

   

#include <iostream>

#include <cassert>

using namespace std;

   

   

//邊信息:兩個頂點和權值

template<typename Weight>

class Edge

{

   

private:

   

int a, b; //邊的兩個頂點ab(如果是有向圖,就默認從頂點a指向頂點b

Weight weight; //邊上的權值

   

public:

   

Edge(int a, int b, Weight weight)

{

this->a = a;

this->b = b;

this->weight = weight;

}

   

   

//默認構造函數

Edge(){}

   

   

~Edge(){}

   

   

int v(){ return a; }

   

   

int w(){ return b; }

   

   

Weight wt() { return weight; }

   

   

//知道邊的一個頂點x,返回另一個頂點

int other(int x)

{

assert(x == a || x == b);

return x == a ? b : a;

}

   

   

//友元函數重載

friend ostream &operator<<(ostream &os, const Edge &e)

{

os << e.a << "-" << e.b << ": " << e.weight;

return os;

}

   

   

bool operator<(Edge<Weight> &e)

{

return weight < e.wt();

}

   

   

bool operator<=(Edge<Weight> &e)

{

return weight <= e.wt();

}

   

   

bool operator>(Edge<Weight> &e)

{

return weight > e.wt();

}

   

   

bool operator>=(Edge<Weight> &e)

{

return weight >= e.wt();

}

   

   

bool operator==(Edge<Weight> &e)

{

return weight == e.wt();

}

};

   

   

#endif

   

   

   

SparseGraph.h:

   

#ifndef SPARSEGRAPH_H

#define SPARSEGRAPH_H

   

#include "Edge.h"

#include <iostream>

#include <vector>

#include <cassert>

using namespace std;

   

   

   

// 稀疏圖 - 鄰接表

template<typename Weight>

class SparseGraph

{

   

private:

   

int n, m; //n m 分別表示頂點數和邊數

bool directed; //directed表示是有向圖還是無向圖

vector<vector<Edge<Weight> *>> g; //g[i]里存儲的就是和頂點i相鄰的所有邊指針

   

public:

   

SparseGraph(int n, bool directed)

{

this->n = n;

this->m = 0;

this->directed = directed;

//g[i]初始化為空的vector

for (int i = 0; i < n; i++)

{

g.push_back(vector<Edge<Weight> *>());

}

}

   

   

~SparseGraph()

{

   

for (int i = 0; i < n; i++)

{

for (int j = 0; j < g[i].size(); j++)

{

delete g[i][j];

}

}

}

   

   

int V(){ return n; }

int E(){ return m; }

   

   

void addEdge(int v, int w, Weight weight)

{

assert(v >= 0 && v < n);

assert(w >= 0 && w < n);

   

g[v].push_back(new Edge<Weight>(v, w, weight));

//1)頂點v不等於頂點w,即不是自環邊

//2)且不是有向圖,即是無向圖

if (v != w && !directed)

{

g[w].push_back(new Edge<Weight>(w, v, weight));

}

   

m++;

}

   

   

//hasEdge()判斷頂點v和頂點w之間是否有邊

//hasEdge()的時間復雜度:O(n)

bool hasEdge(int v, int w)

{

assert(v >= 0 && v < n);

assert(w >= 0 && w < n);

   

for (int i = 0; i < g[v].size(); i++)

{

if (g[v][i]->other(v) == w)

{

return true;

}

}

   

return false;

}

   

   

void show()

{

   

for (int i = 0; i < n; i++)

{

cout << "vertex " << i << ":\t";

for (int j = 0; j < g[i].size(); j++)

{

cout << "{to:" << g[i][j]->w() << ",wt:" << g[i][j]->wt() << "}\t";

}

cout << endl;

}

}

   

   

   

//鄰邊迭代器(相鄰,即 adjacent

//

//使用迭代器可以隱藏迭代的過程,按照一定的

//順序訪問一個容器中的所有元素

class adjIterator

{

private:

   

SparseGraph &G; //圖的引用,即要迭代的圖

int v; //頂點v

int index; //相鄰頂點的索引

   

public:

   

adjIterator(SparseGraph &graph, int v) : G(graph)

{

this->v = v;

this->index = 0;

}

   

   

//要迭代的第一個元素

Edge<Weight> *begin()

{

//因為有可能多次調用begin()

//所以顯式的將index設置為0

index = 0;

//如果g[v]size()不為0

if (G.g[v].size())

{

return G.g[v][index];

}

   

return NULL;

}

   

   

//要迭代的下一個元素

Edge<Weight> *next()

{

index++;

if (index < G.g[v].size())

{

return G.g[v][index];

}

   

return NULL;

}

   

   

//判斷迭代是否終止

bool end()

{

return index >= G.g[v].size();

}

};

};

   

   

#endif

   

   

   

DenseGraph.h:

   

#ifndef DENSEGRAPH_H

#define DENSEGRAPH_H

   

#include "Edge.h"

#include <iostream>

#include <vector>

#include <cassert>

using namespace std;

   

   

   

// 稠密圖 - 鄰接矩陣

template<typename Weight>

class DenseGraph

{

   

private:

   

int n, m; //n m 分別表示頂點數和邊數

bool directed; //directed表示是有向圖還是無向圖

vector<vector<Edge<Weight> *>> g; //二維矩陣,存儲邊指針

   

public:

   

DenseGraph(int n, bool directed)

{

this->n = n;

this->m = 0;

this->directed = directed;

//二維矩陣:nn列,全部初始化為NULL

for (int i = 0; i < n; i++)

{

g.push_back(vector<Edge<Weight> *>(n, NULL));

}

}

   

   

~DenseGraph()

{

for (int i = 0; i < n; i++)

{

for (int j = 0; j < n; j++)

{

if (g[i][j] != NULL)

{

delete g[i][j];

}

}

}

}

   

   

int V(){ return n; }

int E(){ return m; }

   

   

//在頂點v和頂點w之間建立一條邊

void addEdge(int v, int w, Weight weight)

{

assert(v >= 0 && v < n);

assert(w >= 0 && w < n);

   

//如果頂點v和頂點w之間已經存在一條邊,就刪掉,

//之后按照傳入權值重建一條邊,即直接覆蓋

if (hasEdge(v, w))

{

delete g[v][w];

   

//如果是無向圖,還要刪除和主對角線對稱的值

if (!directed)

{

delete g[w][v];

}

   

m--;

}

   

g[v][w] = new Edge<Weight>(v, w, weight);

   

//如果是無向圖,還要在和主對角線對稱處添加值

if (!directed)

{

g[w][v] = new Edge<Weight>(w, v, weight);

}

   

m++;

}

   

   

//hasEdge()判斷頂點v和頂點w之間是否有邊

//hasEdge()的時間復雜度:O(1)

bool hasEdge(int v, int w)

{

assert(v >= 0 && v < n);

assert(w >= 0 && w < n);

return g[v][w] != NULL;

}

   

   

void show()

{

   

for (int i = 0; i < n; i++)

{

for (int j = 0; j < n; j++)

{

if (g[i][j])

{

cout << g[i][j]->wt() << "\t";

}

else

{

cout << "NULL\t";

}

}

cout << endl;

}

}

   

   

//鄰邊迭代器(相鄰,即 adjacent

class adjIterator

{

private:

   

DenseGraph &G; //圖引用,即要迭代的圖

int v; //頂點v

int index; //相鄰頂點的索引

   

public:

   

adjIterator(DenseGraph &graph, int v) : G(graph)

{

this->v = v;

this->index = -1;

}

   

   

//要迭代的第一個元素

Edge<Weight> *begin()

{

//找第一個權值不為NULL的元素,即為要迭代的第一個元素

index = -1;

return next();

}

   

   

//要迭代的下一個元素

Edge<Weight> *next()

{

for (index += 1; index < G.V(); index++)

{

if (G.g[v][index])

{

return index;

}

}

   

return NULL;

}

   

   

//判斷迭代是否終止

bool end()

{

return index >= G.V();

}

};

};

   

   

#endif

   

   

   

ReadGraph.h:

   

#ifndef READGRAPH_H

#define READGRAPH_H

   

#include <iostream>

#include <string>

#include <fstream>

#include <sstream>

#include <cassert>

using namespace std;

   

   

   

//從文件中讀取圖的測試用例

template <typename Graph, typename Weight>

class ReadGraph

{

   

public:

ReadGraph(Graph &graph, const string &filename)

{

   

ifstream file(filename);

string line; //一行一行的讀取

int V, E;

   

assert(file.is_open());

   

//讀取file中的第一行到line

assert(getline(file, line));

//將字符串line放在stringstream

stringstream ss(line);

//通過stringstream解析出整型變量:頂點數和邊數

ss >> V >> E;

   

//確保文件里的頂點數和圖的構造函數中傳入的頂點數一致

assert(V == graph.V());

   

//讀取file中的其它行

for (int i = 0; i < E; i++)

{

   

assert(getline(file, line));

stringstream ss(line);

   

int a, b;

Weight w;

ss >> a >> b >> w;

assert(a >= 0 && a < V);

assert(b >= 0 && b < V);

graph.addEdge(a, b, w);

}

}

};

   

   

#endif

   

   

   

MinIndexHeap.h:

   

#ifndef MININDEXHEAP_H

#define MININDEXHEAP_H

   

#include <iostream>

#include <string>

#include <cassert>

#include <algorithm>

using namespace std;

   

   

   

//最小索引堆:索引從0開始

template<typename Item>

class MinIndexHeap

{

   

private:

Item *data; //指向存儲元素的數組

int *indexes; //指向存儲索引的數組

int *reverse; //指向存儲反向索引的數組

int count;

int capacity;

   

   

//私有函數,用戶不能調用

void shiftUp(int k)

{

//如果新添加的元素小於父節點的元素,則進行交換

while (k > 0 && data[indexes[(k - 1) / 2]] > data[indexes[k]])

{

swap(indexes[(k - 1) / 2], indexes[k]);

reverse[indexes[(k - 1) / 2]] = (k - 1) / 2;

reverse[indexes[k]] = k;

k = (k - 1) / 2;

}

}

   

   

//也是私有函數,用戶不能調用

void shiftDown(int k)

{

//只要當前節點有孩子就進行循環

while (2 * k + 1 < count)

{

// 在此輪循環中,data[indexes[k]]data[indexes[j]]交換位置

int j = 2 * k + 1;

   

// data[indexes[j]]data[indexes[j]]data[indexes[j+1]]中的最小值

if (j + 1 < count && data[indexes[j + 1]] < data[indexes[j]])

{

j += 1;

}

   

if (data[indexes[k]] <= data[indexes[j]])

{

break;

}

   

swap(indexes[k], indexes[j]);

reverse[indexes[k]] = k;

reverse[indexes[j]] = j;

k = j;

}

}

   

   

public:

   

MinIndexHeap(int capacity)

{

data = new Item[capacity];

indexes = new int[capacity];

reverse = new int[capacity];

//初始化reverse數組

for (int i = 0; i < capacity; i++)

{

reverse[i] = -1;

}

//計數器,這里索引等於計數器減一

count = 0;

this->capacity = capacity;

   

}

   

   

~MinIndexHeap()

{

delete []data;

delete []indexes;

delete []reverse;

}

   

   

int size()

{

return count;

}

   

   

bool isEmpty()

{

return count == 0;

}

   

   

void insert(int i, Item item)

{

//防止越界

assert(count <= capacity);

assert(i >= 0 && i <= capacity);

   

data[i] = item;

indexes[count] = i;

reverse[i] = count;

count++;

   

shiftUp(count - 1);

}

   

   

//取出最小的data

Item extractMin()

{

//首先要保證堆不為空

assert(count > 0);

   

Item ret = data[indexes[0]];

swap(indexes[0], indexes[count - 1]);

reverse[indexes[count - 1]] = -1;

reverse[indexes[0]] = 0;

count--;

shiftDown(0);

return ret;

}

   

   

//取出最小的data對應的index

int extractMinIndex()

{

assert(count > 0);

   

//對於外部來說,索引從0開始,所以要減一

int ret = indexes[0];

swap(indexes[0], indexes[count - 1]);

reverse[indexes[count - 1]] = -1;

reverse[indexes[0]] = 0;

count--;

shiftDown(0);

return ret;

}

   

   

Item getMin()

{

assert(count > 0);

return data[indexes[0]];

}

   

   

int getMinIndex()

{

assert(count > 0);

return indexes[0];

}

   

   

bool contain(int i){

assert(i >= 0 && i <= capacity);

//reverse數組在構造函數中都初始化為-1

//所以拿-1做比較

return reverse[i] != -1;

}

   

   

Item getItem(int i)

{

assert(contain(i));

//對於外部來說,索引從0開始,

//對於內部來說,索引從1開始,

//所以要加一

return data[i];

}

   

   

//修改 index 對應的 data

void change(int i, Item newItem)

{

//防止越界和檢查i是否在堆中,

//因為有可能已經取出去了

assert(contain(i));

   

data[i] = newItem;

   

// 找到indexes[j] = i, j表示data[i]在堆中的位置

// 之后嘗試着shiftUp(j)一下, shiftDown(j)一下

//看看能不能向上或向下移動以保持堆的性質

int j = reverse[i];

shiftUp(j);

shiftDown(j);

   

//先用O(1)的時間找到位置,再用O(lgn)的時間完成

//Shift UpShift Down,此時,該函數的時間復雜

//度就是O(lgn)級別的,如果有n個堆操作,總時間

//就是O(n*lgn)

//

//加入了反向查找后,性能得到了巨大的提升

}

   

   

public:

   

//在控制台打印測試用例

void testPrint()

{

   

//限制:只能打印100個元素以內的堆,因為控制台一行的字符數量有限

if (size() >= 100)

{

cout << "Fancy print can only work for less than 100 int";

return;

}

   

//限制:只能打印類型是int的堆

if (typeid(Item) != typeid(int))

{

cout << "Fancy print can only work for int item";

return;

}

   

cout << "The Heap size is: " << size() << endl;

cout << "data in heap: ";

for (int i = 0; i < size(); i++)

{

cout << data[i] << " ";

}

cout << endl;

cout << endl;

   

int n = size();

int max_level = 0;

int number_per_level = 1;

while (n > 0)

{

max_level += 1;

n -= number_per_level;

number_per_level *= 2;

}

   

int max_level_number = int(pow(2, max_level - 1));

int cur_tree_max_level_number = max_level_number;

int index = 0;

for (int level = 0; level < max_level; level++)

{

string line1 = string(max_level_number * 3 - 1, ' ');

   

int cur_level_number = min(count - int(pow(2, level)) + 1,

int(pow(2, level)));

   

bool isLeft = true;

   

for (int index_cur_level = 0; index_cur_level < cur_level_number;

index++, index_cur_level++)

{

putNumberInLine(indexes[index], line1, index_cur_level,

cur_tree_max_level_number * 3 - 1, isLeft);

   

isLeft = !isLeft;

}

cout << line1 << endl;

   

   

if (level == max_level - 1)

{

break;

}

   

   

string line2 = string(max_level_number * 3 - 1, ' ');

for (int index_cur_level = 0; index_cur_level < cur_level_number;

index_cur_level++)

{

putBranchInLine(line2, index_cur_level, cur_tree_max_level_number * 3 - 1);

}

   

cout << line2 << endl;

   

cur_tree_max_level_number /= 2;

}

}

   

   

   

private:

   

void putNumberInLine(int num, string &line, int index_cur_level,

int cur_tree_width, bool isLeft)

{

   

int sub_tree_width = (cur_tree_width - 1) / 2;

   

int offset = index_cur_level * (cur_tree_width + 1) + sub_tree_width;

   

assert(offset + 1 < line.size());

   

if (num >= 10)

{

line[offset + 0] = '0' + num / 10;

line[offset + 1] = '0' + num % 10;

}

else

{

if (isLeft)

line[offset + 0] = '0' + num;

else

line[offset + 1] = '0' + num;

}

}

   

   

void putBranchInLine(string &line, int index_cur_level, int cur_tree_width)

{

   

int sub_tree_width = (cur_tree_width - 1) / 2;

   

int sub_sub_tree_width = (sub_tree_width - 1) / 2;

   

int offset_left = index_cur_level * (cur_tree_width + 1) + sub_sub_tree_width;

   

assert(offset_left + 1 < line.size());

   

int offset_right = index_cur_level * (cur_tree_width + 1) + sub_tree_width

+ 1 + sub_sub_tree_width;

   

assert(offset_right < line.size());

   

line[offset_left + 1] = '/';

line[offset_right + 0] = '\\';

}

};

   

   

#endif

   

   

   

Dijkstra.h:

   

#ifndef DIJKSTRA_H

#define DIJKSTRA_H

   

#include "Edge.h"

#include "MinIndexHeap.h"

#include <iostream>

#include <vector>

#include <stack>

using namespace std;

   

   

   

//Dijkstra 算法實現最短路徑

template<typename Graph, typename Weight>

class Dijkstra

{

   

private:

   

Graph &G; //圖的引用,即要進行操作的圖

int s; //起始頂點 ss source

Weight *distTo; //起始頂點 s 到每一個頂點的當前最短距離

bool *marked; //對已經找到最短路徑的頂點進行標識

vector<Edge<Weight>*> from; //經由哪條邊到達了當前頂點

   

public:

   

Dijkstra(Graph &graph, int s) :G(graph)

{

   

this->s = s;

distTo = new Weight[G.V()];

marked = new bool[G.V()];

   

for (int i = 0; i < G.V(); i++)

{

//由於不知道 distTo 數組中元素的具體類型,

//所以使用模板類型Weight的默認構造函數,

//如果指定的模板為 int,會被初始化為 0

distTo[i] = Weight();

marked[i] = false;

from.push_back(NULL);

}

   

MinIndexHeap<Weight> ipq(G.V());

   

// Dijkstra

//

//對起始頂點 s 到自身的最短距離進行初始化,

//如果指定的模板為 int,會被初始化為 0

distTo[s] = Weight();

ipq.insert(s, distTo[s]);

marked[s] = true;

   

//只要最小索引堆不為空,就進行循環

while (!ipq.isEmpty())

{

//從最小索引堆中找到當前最短距離最小的頂點 v

//此時,distTo[v] 就是起始頂點 s 到頂點 v

//最短距離

int v = ipq.extractMinIndex();

 

marked[v] = true;

   

//注意:聲明迭代器時,前面還要加 typename,表明

//adjIterator Graph 中的類型,而不是成員變量

typename Graph::adjIterator adj(G, v);

//對當前頂點 v 的所有相鄰頂點依次進行松弛操作

for (Edge<Weight> *e = adj.begin(); !adj.end(); e = adj.next())

{

//當前頂點 v 的相鄰頂點 w

int w = e->other(v);

//如果頂點 w 沒有被標識,即從起始頂點

// s 到相鄰頂點 w 的最短路徑還沒有找到

if (!marked[w])

{

//1)如果還沒有邊到達相鄰頂點 w

//2)或:"經過"當前頂點 v 到相鄰頂點 w 所得到的

//路徑小於"不經過"當前頂點 v 到相鄰頂點 w 所得到

//的路徑,就進行松弛操作

if (from[w] == NULL || distTo[v] + e->wt() < distTo[w])

{

distTo[w] = distTo[v] + e->wt();

from[w] = e;

   

//判斷最小索引堆中是否包含頂點 w

//如果包含就更新,否則直接插入

if (ipq.contain(w))

{

ipq.change(w, distTo[w]);

}

else

{

ipq.insert(w, distTo[w]);

}

}

}

}

}

}

   

   

~Dijkstra()

{

delete []distTo;

delete []marked;

}

   

   

//頂點 s 到頂點 w 的最短距離

Weight shortestPathTo(int w)

{

assert(w >= 0 && w < G.V());

return distTo[w];

}

   

   

//判斷頂點 s 到頂點 w 是否有路徑,

//即判斷二者是否連通即可

bool hasPathTo(int w)

{

assert(w >= 0 && w < G.V());

return marked[w];

}

   

   

//找到從頂點 s 到頂點 w 的最短路徑的邊的組成:通過from數組

//從頂點 w 倒推回去,並存儲在棧中,最后再從棧中轉存到向量中

void shortestPath(int w, vector<Edge<Weight>> &vec)

{

   

assert(w >= 0 && w < G.V());

   

stack<Edge<Weight>*> s;

   

Edge<Weight> *e = from[w];

   

//直到倒推到起始頂點,對於有向圖

//來說,e->v() 即一條邊的起點

while (e->v() != this->s)

{

s.push(e);

e = from[e->v()];

}

s.push(e);

   

//只要棧不為空,就將棧頂元素放入

//向量中,並出棧

while (!s.empty())

{

e = s.top();

vec.push_back(*e);

s.pop();

}

}

   

   

//打印從頂點 s 到頂點 w 的最短路徑

void showPath(int w)

{

   

assert(w >= 0 && w < G.V());

   

vector<Edge<Weight>> vec;

shortestPath(w, vec);

for (int i = 0; i < vec.size(); i++)

{

cout << vec[i].v() << " -> ";

if (i == vec.size() - 1)

{

cout << vec[i].w() << endl;

}

}

}

};

   

   

#endif

   

   

   

main.cpp:

   

#include "SparseGraph.h"

#include "DenseGraph.h"

#include "ReadGraph.h"

#include "Dijkstra.h"

#include <iostream>

using namespace std;

   

   

   

int main()

{

   

string filename = "testG1.txt";

int V = 5;

   

//稀疏圖:通常在實際生活中所處理的圖,稀疏圖相對更多

SparseGraph<int> g = SparseGraph<int>(V, true);

//SparseGraph<int> g = SparseGraph<int>(V, false);

ReadGraph<SparseGraph<int>, int> readGraph(g, filename);

   

cout << "Test Dijkstra:" << endl << endl;

Dijkstra<SparseGraph<int>, int> dij(g, 0);

for (int i = 1; i < V; i++)

{

cout << "Shortest Path to " << i << " : "

<< dij.shortestPathTo(i) << endl;

   

dij.showPath(i);

   

cout << "----------" << endl;

}

   

system("pause");

return 0;

}

   

   

//每一次插入和更新,都使用 logV 的時間復雜度,而整個

//Dijkstra 算法過程中,要對所有的邊進行一次遍歷,最

//終使得算法的時間復雜度是 O(E*logV) 這個級別的

   

   

運行一覽:

   

   

   

   

其中,testG1.txt 的內容如下:

   

   

   

該文件可以分成兩個部分:

   

1)第一行:兩個數字分別代表頂點數和邊數

   

2)其它行:每一行的前兩個數字表示一條邊,第三個數字表示權值

   

   

   

   

   

   

   

   

   

【made by siwuxie095】


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM