#ifndefINC_03_IMPLEMENTATION_OF_DIJKSTRA_SPARSEGRAPH_H#defineINC_03_IMPLEMENTATION_OF_DIJKSTRA_SPARSEGRAPH_H#include<iostream>#include<vector>#include<cassert>#include"Edge.h"
using namespace std;// 稀疏图 - 邻接表
template<typename Weight>
class SparseGraph{
private:int n, m;// 节点数和边数
bool directed;// 是否为有向图
vector<vector<Edge<Weight>*>> g;// 图的具体数据
public:// 构造函数SparseGraph(int n , bool directed){assert(n >=0);
this->n = n;
this->m =0;// 初始化没有任何边
this->directed = directed;// g初始化为n个空的vector, 表示每一个g[i]都为空, 即没有任和边
g = vector<vector<Edge<Weight>*>>(n, vector<Edge<Weight>*>());}// 析构函数~SparseGraph(){for(int i =0; i < n ; i ++)for(int j =0; j < g[i].size(); j ++)
delete g[i][j];}intV(){return n;}// 返回节点个数intE(){return m;}// 返回边的个数// 向图中添加一个边, 权值为weightvoidaddEdge(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));if( v != w &&!directed )
g[w].push_back(new Edge<Weight>(w, v, weight));
m ++;}// 验证图中是否有从v到w的边
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;}// 显示图的信息voidshow(){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;}}// 邻边迭代器, 传入一个图和一个顶点,// 迭代在这个图中和这个顶点向连的所有边
class adjIterator{
private:
SparseGraph &G;// 图G的引用int v;int index;
public:// 构造函数adjIterator(SparseGraph &graph,int v):G(graph){
this->v = v;
this->index =0;}~adjIterator(){}// 返回图G中与顶点v相连接的第一个边
Edge<Weight>*begin(){
index =0;if( G.g[v].size())return G.g[v][index];// 若没有顶点和v相连接, 则返回NULLreturnNULL;}// 返回图G中与顶点v相连接的下一个边
Edge<Weight>*next(){
index +=1;if( index < G.g[v].size())return G.g[v][index];returnNULL;}// 查看是否已经迭代完了图G中与顶点v相连接的所有顶点
bool end(){return index >= G.g[v].size();}};};#endif//INC_03_IMPLEMENTATION_OF_DIJKSTRA_SPARSEGRAPH_H
#ifndefINC_03_IMPLEMENTATION_OF_DIJKSTRA_READGRAPH_H#defineINC_03_IMPLEMENTATION_OF_DIJKSTRA_READGRAPH_H#include<iostream>#include<string>#include<fstream>#include<sstream>#include<cassert>
using namespace std;// 读取有权图、
template <typename Graph, typename Weight>
class ReadGraph{
public:// 从文件filename中读取有权图的信息, 存储进图graph中ReadGraph(Graph &graph,const string &filename){
ifstream file(filename);
string line;int V, E;assert(file.is_open());// 第一行读取图中的节点个数和边的个数assert(getline(file,line));
stringstream ss(line);
ss >> V >> E;assert( graph.V()== V );
cout << V <<" "<< E << endl;// 读取每一条边的信息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//INC_03_IMPLEMENTATION_OF_DIJKSTRA_READGRAPH_H
#ifndefINC_03_IMPLEMENTATION_OF_DIJKSTRA_INDEXMINHEAP_H#defineINC_03_IMPLEMENTATION_OF_DIJKSTRA_INDEXMINHEAP_H#include<iostream>#include<algorithm>#include<cassert>
using namespace std;// 最小索引堆
template<typename Item>
class IndexMinHeap{
private:
Item *data;// 最小索引堆中的数据int*indexes;// 最小索引堆中的索引, indexes[x] = i 表示索引i在x的位置int*reverse;// 最小索引堆中的反向索引, reverse[i] = x 表示索引i在x的位置int count;int capacity;// 索引堆中, 数据之间的比较根据data的大小进行比较, 但实际操作的是索引voidshiftUp(int k ){while( k >1&& data[indexes[k/2]]> data[indexes[k]]){swap( indexes[k/2], indexes[k]);
reverse[indexes[k/2]]= k/2;
reverse[indexes[k]]= k;
k /=2;}}// 索引堆中, 数据之间的比较根据data的大小进行比较, 但实际操作的是索引voidshiftDown(int k ){while(2*k <= count ){int j =2*k;if( j +1<= count && data[indexes[j]]> data[indexes[j+1]])
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:// 构造函数, 构造一个空的索引堆, 可容纳capacity个元素IndexMinHeap(int capacity){
data = new Item[capacity+1];
indexes = new int[capacity+1];
reverse = new int[capacity+1];for(int i =0; i <= capacity ; i ++)
reverse[i]=0;
count =0;
this->capacity = capacity;}~IndexMinHeap(){
delete[] data;
delete[] indexes;
delete[] reverse;}// 返回索引堆中的元素个数intsize(){return count;}// 返回一个布尔值, 表示索引堆中是否为空
bool isEmpty(){return count ==0;}// 向最小索引堆中插入一个新的元素, 新元素的索引为i, 元素为item// 传入的i对用户而言,是从0索引的voidinsert(int index, Item item){assert( count +1<= capacity );assert( index +1>=1&& index +1<= capacity );
index +=1;
data[index]= item;
indexes[count+1]= index;
reverse[index]= count+1;
count++;shiftUp(count);}// 从最小索引堆中取出堆顶元素, 即索引堆中所存储的最小数据
Item extractMin(){assert( count >0);
Item ret = data[indexes[1]];swap( indexes[1], indexes[count]);
reverse[indexes[count]]=0;
reverse[indexes[1]]=1;
count--;shiftDown(1);return ret;}// 从最小索引堆中取出堆顶元素的索引intextractMinIndex(){assert( count >0);int ret = indexes[1]-1;swap( indexes[1], indexes[count]);
reverse[indexes[count]]=0;
reverse[indexes[1]]=1;
count--;shiftDown(1);return ret;}// 获取最小索引堆中的堆顶元素
Item getMin(){assert( count >0);return data[indexes[1]];}// 获取最小索引堆中的堆顶元素的索引intgetMinIndex(){assert( count >0);return indexes[1]-1;}// 看索引i所在的位置是否存在元素
bool contain(int index ){return reverse[index+1]!=0;}// 获取最小索引堆中索引为i的元素
Item getItem(int index ){assert(contain(index));return data[index+1];}// 将最小索引堆中索引为i的元素修改为newItemvoidchange(int index , Item newItem ){assert(contain(index));
index +=1;
data[index]= newItem;shiftUp( reverse[index]);shiftDown( reverse[index]);}};#endif//INC_03_IMPLEMENTATION_OF_DIJKSTRA_INDEXMINHEAP_H
#include<iostream>#include<vector>#include<cassert>#include<stack>#include"Edge.h"#include"IndexMinHeap.h"#include"SparseGraph.h"#include"ReadGraph.h"
using namespace std;// Dijkstra算法求最短路径
template<typename Graph, typename Weight>
class Dijkstra{
private:
Graph &G;// 图的引用int s;// 起始点
Weight *distTo;// distTo[i]存储从起始点s到i的最短路径长度
bool *marked;
vector<Edge<Weight>*> from;// from[i]记录最短路径中, 到达i点的边是哪一条 可以用来恢复整个最短路径
public :// 构造函数, 使用Dijkstra算法求最短路径Dijkstra(Graph &graph,int s):G(graph){// 算法初始化assert(s >=0&& s < G.V());
this->s = s;
distTo = new Weight[G.V()];
marked = new bool[G.V()];for(int i=0; i < G.V(); i++){
distTo[i]=Weight();
marked[i]= false;
from.push_back(NULL);}// 使用索引堆记录当前找到的到达每个顶点的最短距离
IndexMinHeap<Weight>ipq(G.V());// 对于起始点s进行初始化
distTo[s]=Weight();
from[s]= new Edge<Weight>(s, s,Weight());
ipq.insert(s, distTo[s]);
marked[s]= true;// 其他点while(!ipq.isEmpty()){int v = ipq.extractMinIndex();// distTo[v]就是s到v的最短距离
marked[v]= true;// 对v的所有相邻节点进行更新
typename Graph::adjIterator adj(G, v);for(Edge<Weight>* e = adj.begin();!adj.end(); e = adj.next()){int w = e->other(v);// 如果从s点到w点的最短路径还没有找到if(!marked[w]){// 如果w点以前没有访问过,// 或者访问过, 但是通过当前的v点到w点距离更短, 则进行更新if(from[w]==NULL|| distTo[v]+e->wt()< distTo[w]){
distTo[w]= distTo[v]+e->wt();
from[w]= e;if(ipq.contain(w))
ipq.change(w, distTo[w]);else
ipq.insert(w, distTo[w]);}}}}for(int i =0; i < G.V(); i++){
cout<< marked[i]<<endl;}}~Dijkstra(){
delete[] distTo;
delete[] marked;
delete from[0];};// 判断从s点到w点是否联通
bool hasEdgTo(int w){assert( w >=0&& w < G.V());return marked[w];}// 返回从s点到w点的最短路径长度
Weight shortestPathTo(int w){assert( w >=0&& w < G.V());assert(hasEdgTo(w));return distTo[w];}// 寻找从s到w的最短路径, 将整个路径经过的边存放在vec中voidshortestPath(int w, vector<Edge<Weight>>&vec){assert( w >=0&& w < G.V());assert(hasEdgTo(w));// 通过from数组逆向查找到从s到w的路径, 存放到栈中
stack<Edge<Weight>*> s;
Edge<Weight>*e = from[w];while(e->v()!= this->s){
s.push(e);
e = from[e->v()];}
s.push(e);//从栈中依次取出元素, 获得顺序的从s到w的路径while(!s.empty()){
e = s.top();
vec.push_back(*e);
s.pop();}}// 打印出从s点到w点的路径voidshowPath(int w){assert( w >=0&& w < G.V());assert(hasEdgTo(w));
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;}}};intmain(){
string filename ="/Users/ericli/CLionProjects/lijunshi/day16/testG1.txt";int V =5;
SparseGraph<int> g = SparseGraph<int>(V, true);
ReadGraph<SparseGraph<int>,int>readGraph(g, filename);// g.show();
Dijkstra<SparseGraph<int>,int>dij(g,0);
dij.showPath(4);return0;}