亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++ Dijkstra算法之求圖中任意兩頂點的最短路徑

 更新時間:2021年11月19日 16:52:46   作者:是牛大春呀  
這篇文章主要為大家詳細介紹了用C++經(jīng)典算法-Dijkstra算法求任意兩頂點之間的最短路徑,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

Dijkstra算法是圖中找任意兩點中最短路徑的一種經(jīng)典算法。

重點的步驟總結如下:

1.算法采用了并查集 (之后都叫它為 最短路徑頂點集 ):即每次都找離開始頂點距離最短的頂點,然后把該頂點加入最短路徑頂點集中(已經(jīng)加入最短路徑頂點集里的那些頂點 下一次就會跳過它了,并且,在頂點集里 任意兩個頂點間的距離 都已經(jīng)是最短)
2.用來記錄從源點(開始頂點) 到vi (0<=i<=numVertices) 的最短距離 的數(shù)組dist[numVertices] ,并且這個數(shù)組的元素值是會不斷變化的,為什么呢,因為,這個最短距離無非兩種情況。
第一種:開始頂點v直接到達目的頂點X的距離。
第二種:開始頂點v先到達中間頂點Vk(Vk可能不止一個)再到目的頂點的距離。
要么是第一種情況最短,要么是第二種情況最短,因此需要再這兩者間選擇一個最小值作為最短路徑。
dist[w] = min {dist[w] ,dist[k] + this->getWeight(k,w)};
3.路徑數(shù)組path[],這個數(shù)組 保存了從源點到終點vi 的最短路徑上該頂點的前驅頂點的序號,即:會記錄最短路徑 某個頂點的上一個 頂點是什么,比如說 path[4]的值是2,那么 4的上一個頂點 就是2 ,path[2]的頂點比如是3 那么2的上一個頂點就是3 ,繼續(xù), 如果path[3]是0那么 這個路徑為: , 因此,當Dijkstra算法結束的時候,我們只需要從最后那個頂點開始 path[endVertexce] 就可以得到它上一個頂點的下標,然后一直找,直到找到源點,那么這個路徑就輸出完了.

Dijkstra算法求帶權有向圖單元最短路徑的示例過程圖如下:

圖a為 本次的示例圖,然后假如要從v0出發(fā),去找頂點v4的最短距離.首先,我們可以看到圖b 伸出三條虛線,是什么意思呢?就是因為v0 和這三個頂點都連通,然后,找一個最短和v0相連的頂點,發(fā)現(xiàn)是v1,權值是10,然后接下來的要做什么呢??接下來就是重點了,要從v1出發(fā),去尋找所有與v1 相連的頂點,如果源點到 v1 后再到這些頂點的距離 比 源點直接到 這些 和v1相連的頂點 的距離 短的話
就要重新修改v0到 vx的值(x為任意與v1相連的頂點),即v0—>v1—>v2,一開始v0不能直接到v2所以dist[2]=∞,但是v0->v1->v2的值卻為60,因此dist[2]的值就改為60,即v0通過“某條路徑”抵達 v2的當前最短路徑就是60,為什么說是當前呢,因為等下可能還有其他 未加入最短路徑頂點集 的某個頂點,他可能從源點,再經(jīng)過它 再到v2 ,比 從源點出發(fā)到 v1 再到v2的距離更小!(比如說v3)。接著重復以上操作:在未加入"最短路徑頂點集" 里 找一個離源點最近的 頂點,然后讓它加入”最短路徑頂點集“里 因為剛才加入的是v1,它的權值是10 ,然后v1里沒有能夠到達v3的邊,所以,v3的值沒有改變,如果v3的值要改變的話:當且僅當從源點 到最小權值頂點再到 v3 因此,v0到v3已經(jīng)是最小的路徑了,因此,把它加入 最短路徑頂點集里, 加入到最短路徑頂點集里,任意兩個頂點之間的距離 都已經(jīng)是最短路徑, v3加到頂點集之后要做的事情 還是和剛才那樣:不斷去尋找和它相連接的頂點Vx,然后比較 v0直接到Vx的距離是否比 v0 先到v3再到 Vx的距離大,如果是,做兩件事情:

① 修改dist[x] 的值(即v0通過某一條路徑到達Vx的最短路徑 這里如果前提條件成立 這條路徑為: v0—>v3—>Vx).
②修改路徑數(shù)組path[x]=index(v3)=3 (下標0對應v0,下標1對應v1…),即讓x這個位置的頂點的前驅的下標索引為3,即v3的下標索引.

Dijkstra算法的思想就是像上述一樣,未完成的步驟留給讀者完成。

具體測試代碼如下(有些代碼與Dijkstra算法無關,代碼是基于之前實現(xiàn)的代碼,如果是DevC++ 編譯器,可以按住Ctrl +鼠標左鍵 點擊主函數(shù)的測試代碼的函數(shù),可以跳到對應 的函數(shù)代碼體,見諒見諒.)

本次路徑的輸出利用了棧,使得路徑可以按從起點到終點 按順序 輸出。

本次測試圖如下:

代碼如下:

#include<iostream>
#include<cstring>
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
const long long int maxWeight = RAND_MAX; //無窮大的值
const int DefaultVertices = 30;	//最大頂點數(shù)不妨設為30 
template <class T, class E>
class Graph {
protected:
    int maxVertices=10;//圖中最大頂點數(shù)
    int numVertices=0;//圖中當前頂點數(shù)
    int numEdges=0;//圖中當前邊數(shù)
    bool direction=false;//圖中邊的是否有方向
    bool withCost=false;//圖中的邊是否帶權
    //返回頂點名vertex的序號(從0開始)
	int getVertexPos (T vertex);	
public:   
	void DFS (const T& v)
	{
		
	} 
	
	void BFS (const T& v)
	{
	} 
                                     
    Graph(int sz , bool direct, bool cost); //構造函數(shù)
    ~Graph()//析構函數(shù) 
	{}	
			//析構函數(shù)
    bool GraphEmpty () const	//判圖是否為空,因為不需要修改,因此設置為常成員函數(shù) 
    {
	  return numEdges == 0;  
	}
    bool GraphFull() const;         //判圖是否為滿
    //返回圖中當前頂點數(shù)
   	int NumberOfVertices () 
	{ 
	   return numVertices;
	}
    //返回圖中當前邊數(shù)
	int NumberOfEdges ()
	{ 
	 return numEdges;
	}
	//取回序號為 i 的頂點值(頂點名)
	virtual T getValue (int i){
	}
    //取頂點序號為v1,v2的邊上權值
    virtual E getWeight (int v1, int v2){
	}   
  	//取頂點 v 的第一個鄰接頂點序號
    virtual int getFirstNeighbor (int v){
	}
   //返回頂點 v 和鄰接頂點 w 的下一個鄰接頂點序號
	virtual int getNextNeighbor (int v, int w){
	}
    //插入新頂點, 點名為vertex
	virtual bool insertVertex (const T vertex){
	}
    //插入新邊(v1,v2), 權值cost
	virtual bool insertEdge (T v1, T v2, E cost){
	}
    //刪除名為 v 的頂點和所有與它相關聯(lián)的邊
	virtual bool removeVertex (T v){
	}
    //在圖中刪除邊(v1,v2)
	virtual bool removeEdge (T v1, T v2){
	}	
};


template<class T , class E>
Graph<T,E>::Graph (int sz , bool direct, bool cost)
{
	 sz = DefaultVertices,  direct=false,  cost=false;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


template <class T, class E> // 
class Edge_Vertices {		   //邊結點的類定義
public:
    int dest;		   //邊的鄰接(終)點序號
   	E cost;		   //邊上的權值
   	Edge_Vertices<T, E>* link;//下一個連接頂點 
	Edge_Vertices (int num=0, Edge_Vertices<T, E>* next=NULL, E weight=NULL) //構造函數(shù)
    : dest (num), link (next), cost (weight) { }
   	bool operator != (Edge_Vertices<T, E>& R) const
	{  return dest != R.dest;  } //重載!=運算符,判邊不等
}; 



template <class T, class E> 
class Vertex {	      //頂點的類定義
public:
    T data;		      //源頂點的名字(數(shù)據(jù))
	Edge_Vertices<T, E>* next=NULL; //next指針用來連接頂點 
};

//鄰接表圖的類定義繼承圖類
template <class T, class E> 
class Graphlnk : public Graph<T, E>{
 public: 
   void Enter (void);  //輸入圖中頂點和邊信息
   void Print (void);   //輸出圖中頂點和邊信息
    //源頂點表 (各邊鏈表的源頂點)
    Vertex<T, E>* NodeTable;
    int *path;
    int *dist;
    //返回名為vertex的頂點在圖中的序號(從0開始),
	 //若未找到,則返回-1
	int getVertexPos (const T vertx) 	//找到目標頂點所在的序號 期中 vertx 參數(shù)為string 類型 
	{   for (int i = 0; i < this->numVertices; i++)
	        if (NodeTable[i].data == vertx) return i;
 	     return -1; 
	}             //構造函數(shù)
    Graphlnk( int sz=DefaultVertices, bool direct=false, bool cost=false ); 
   	~Graphlnk(); //析構函數(shù)
    T getValue (int i) { //取序號為 i 的頂點名
	    return (i >= 0 && i < this->numVertices) ? 
             NodeTable[i].data : NULL;
    }
	E getWeight (int v1, int v2);	//取邊(v1,v2)權值 
	bool insertVertex (const T& vertex); //插入名為vertex的新頂點
    bool removeVertex (int v); //刪除序號為 v 的頂點
    bool insertEdge (T in ,T out, E cost); //插入新邊
	bool removeEdge (int v1, int v2); //刪除邊(v1,v2)
    int getFirstNeighbor (int v);//取頂點v的首鄰接頂點   ###本次代碼這兩條都沒有用到 
    int getNextNeighbor (int v, int w);//取w下一鄰接點   ###本次代碼這兩條都沒有用到 
    void DFS(const T &v);
     void BFS(const T &v);
     void DFS (int v, bool visited[]);
     void BFS(int v , bool visited[]);
     void Connected_Component();
     void Dijkstra (int v );
     void Print(int v, int w);
};
// 輸出頂點 v 到頂點 w 的最短路徑
//和最短路徑長度。int* path;
// E** dist; 為類數(shù)據(jù)成員
template<typename T, typename E>
void Graphlnk<T,E>::Print(int v, int w)//輸出從V 到W 的最短路徑 
 {  //因為v和w都是 int 型,即輸入類型的下標,因此,要用getValue 獲得原來的輸入的頂點名字 
	cout<<"始頂點 "<<this->getValue(v)<<" 到終頂點 "<<this->getValue(w)<<" 的最短路徑為:"<<endl;
	int current=w, previous;
	stack<char > Road;//定義一個char類型的棧,因為是用頂點回溯 (即從 最后的一個頂點 往前找前驅頂點的,因此我們可以用棧(后進先出的特性))
	//用棧后進先出的特性可以 在出棧的時候 按順序輸出從 開始頂點到目的頂點的  路徑 ,如果對輸出沒有要求的話就不需要棧,可以直接輸出
	//不過輸出的結果會倒置(從目的頂點 一步一步往回走 ,直到找到開始頂點) 
	stack<int >Road_Weight;//如果 頂點 要按順序輸出,還想輸出 這兩個頂點間的權值的話,那么這些權值也是倒置的,因此權值也需要利用棧的特性輸出 
	Road.push(this->getValue(w)[0]);//因為this->getValue(w) 返回的是一個string 類型, 本次測試的頂點都是要么是單個數(shù)字名字,或者單個字母名字
	// 因為是char 類型,因此只能單個字母進去,所以this->getValue(w)[0] 就是取第一個 字母/數(shù)字 
	while( current!=v )//如果這個頂點不等于 開始頂點的話 
	{
		previous=path[current];//找它的上一個頂點 
		Road.push(this->getValue(path[current])[0]);//名字入棧 
		Road_Weight.push(this->getWeight(previous,current));//權值入棧 
		current=previous; 
	}
	string  p;//string 變量 p 用來記錄前面的頂點和 做臨時變量 
	string  e;//string 變量 e 用來輸出頂點名字 
	int q;
	int x=1;//一個標志 
	int i = 1;//第i步 
	while(Road.empty()!=true)
	{  
	//這個if 語句只運行一次,為什么呢,考慮到 奇數(shù)個頂點的話,那么,最短路徑至少有兩個頂點,如果第一次就把頂點輸出完了
	//那么后面的if語句都不會運行了,但是如果 是奇數(shù)個頂點呢? 奇數(shù)個頂點的話,程序編譯不會出錯,但是運行會中斷, 即棧已經(jīng)空了,不能彈棧了
	//因此,執(zhí)行一次這個if語句 就開始 對頂點一個 一個的彈棧,而不是 兩個兩個的彈 
	   if(x)
	   {
	   p=Road.top();
	   Road.pop();
	   q=Road_Weight.top();
	   Road_Weight.pop();
	      e=Road.top();
	   Road.pop();
	   x=0;//讓if 語句為假 
	    cout<<"第"<<i++<<"步為:  "<<"頂點"<<p<<"-->到頂點"<<e<<"  長度為: "<<q<<endl;
	
		}
	
	 
	   if(Road.empty()!=true)//如果棧不為空 
	   {
	   	p=e;//因為這個p在后面不再 用來接收 棧彈出的頂點,而是充當一個臨時變量-------用上一個頂點 的末頂點 進行覆蓋 
	   e=Road.top();//取棧首頂點 
	   Road.pop();// 彈棧 
	    q=Road_Weight.top();//取棧里的首權值 
	   Road_Weight.pop();
	   cout<<"第"<<i++<<"步為:  "<<"頂點"<<p<<"-->到頂點"<<e<<"  長度為: "<<q<<endl;	
	   }
	   
	}
	cout<<"最短路徑總長度為:"<<dist[w]<<endl;//輸出最短路徑長度 
}

template<class T,class E>
//Graphlnk是一個帶權有向圖.數(shù)據(jù)成員E dist[v][j],
//0≤j<n, 是當前求到的從頂點v到 j的最短路 徑長度,
//int path[v][j],0≤j<n, 存放求到的最短路徑
void Graphlnk<T,E>::Dijkstra (int v  )//Dijkstra算法,求得圖任意兩點間的 最短路徑 
{ 	int k;//變量k 
	bool *s = new bool[this->numVertices];//并查集s,標記頂點是否已經(jīng)在最短路徑的頂點集里 
	this->path=new int [this->numVertices];//路徑數(shù)組(跳躍式,要用回溯才能找出整個完整的路徑) 
	this->dist= new int [this->numVertices];//源點v到任意頂點vi的最短路徑數(shù)組, 
	for(int i = 0 ; i < this->numVertices;i++)//初始化 
	{	
		s[i]=false;//把所有的頂點都標記為:未在最短路徑頂點集 
		if(this->getWeight(v,i)!=maxWeight)//如果有v和vi間如果有權值的話,那么,就是說,這兩點是連通的。 
		// 既然兩點是連通的,那么,這個路徑就有可能是最短的。 
		this->dist[i]=this->getWeight(v,i);
		else //否則?就是沒有連通咯,就用maxWeight賦值給他,即:如果dist[i]==maxWeight那么 就判定為兩點是不連通的 
		dist[i]=maxWeight;	
		if(this->dist[i]<maxWeight||v==i)//如果有邊連通,或者i==v 
		this->path[i]=v;//就讓i這個頂點的前驅 是v (如果v==i 就讓自己指向自己) 
		else 
		path[i]=-1;// 否則vi和 v 沒有路,即兩點間不連通  
	}
	dist[v]=0;//首先,規(guī)定,v->v 即自己到自己的距離是0 
	s[v]=true;//將開始頂點v放入并查集數(shù)組中 
	int min;//最小路徑的一個值 
	for(int i = 1; i <this->numVertices;i++)
	{
		 min = maxWeight;//首先要讓這個最小值足夠大,然后后面的那些路徑權值才會比這個值小然后把它替換為真正的"最小值" 
		for(int j=0;j<this->numVertices;j++)
		if(!s[j]&&dist[j]<min)//如果這個頂點沒有在最小路徑的頂點集里面,則判定是否連通
		//如果連通的話,就從這些 連通的頂點間找到一組最小的連通 頂點。 
		{
			k=j;
			min=dist[j];
		}
		s[k]=true;//找一個:若在原來路徑上 添加一個頂點,首先,這個頂點在最短路徑頂點集和之外, 其次,這個頂點沿著其余頂點回到開始頂點路徑最短 
		for(int w = 0 ; w <this->numVertices;w++)
		//從這個新加入的頂點Vnew出發(fā),不斷的去找和它相連接的頂點vi(i取任意正整數(shù),即頂點可能不知有一個,可能是多個)
		//然后看v到vi的路徑短一點還是,v到Vnew再到Vi,如果是v到Vnew 再到vi 比 直接從v到vi更短的話,那么就替換 v到Vw的最小距離dist[w]
		//并且規(guī)定w的前驅頂點為k ,即:path[w]=k; (此時w還沒有在最短路徑頂點集合里面) 
		//由此我們可以知道:既然每加入一個 頂點到 最短路徑頂點集里面,都會執(zhí)行這段代碼。 
		//然后 都會從剛加入的那個頂點去 找遍所有 和它關聯(lián)的頂點,所以,如果說這個加入的頂點如果 和目的頂點X相連,那么加入這個頂點后
		// 執(zhí)行下面的代碼,就可以求出當前從始頂點到目的頂點X的最短路徑,為什么是當前?因為這個最小是當前最小的,因為還有其他頂點未加入頂點集
		//即有可能從 v出發(fā) 經(jīng)過某個 未加入  最短路徑頂點集合里的頂點 再到X 的路徑大小 比 從v到原先那個加入  最短路徑頂點集合里的頂點 再到x 的路徑要小
		//所以,下面的代碼每次執(zhí)行都會求得 一個臨時的最短路徑,如果頂點都加入完了,那么,自然是最短路徑了  
		if(!s[w]&&dist[w]>(dist[k]+this->getWeight(k,w))&&this->getWeight(k,w)!=-1)
		{
			dist[w]=dist[k]+this->getWeight(k,w);// 
			path[w]=k;//讓w頂點的前驅是k 
		}
		
	}
	delete []s ;//刪除標記數(shù)組
	//輸出代碼可以在函數(shù)體里,也可以 加一個print函數(shù) 在函數(shù)體外 
//	cout<<"始頂點 "<<v<<" 到終頂點 "<<w<<" 的最短路徑為:終頂點 "<< w;
//	int current=w, previous;
//	while( current!=v )
//	{
//		previous=path[current];
//		cout<<"路徑"<<path[current];
//		cout<<"最小距離"<<dist[current];
//		cout<<" <-權值[ "<<getWeight(previous,current)<<" ]-頂點 "<<previous;
//		current=previous;
//	}
//	cout<<"。最短路徑總長度為:"<<dist[w]<<endl;
}

template<class T, class E>
void Graphlnk<T,E>::Connected_Component()//分別輸出連通分量 和輸出有向圖中連通分量的個數(shù) 
{	int Connected_Component_numbers=0;
	bool visited[this->numVertices];
	for(int i = 0 ; i <this->numVertices;i++)
		visited[i]=false;//初始化這個visited 數(shù)組
	for(int i =0; i < this->numVertices;i++)
	{
	if(!visited[i])//如果這個結點沒有被訪問過
	{
		cout<<"從"<<this->getValue(i)<<"開始的連通分量為:";
		this->DFS(i,visited);
		cout<<endl;
		Connected_Component_numbers+=1;
	 }
	}
	if(this->direction==true)
	cout<<"此有向圖的連通分量為:"<<Connected_Component_numbers<<endl;
	else 
	cout<<"此無向圖的連通分量為:"<<Connected_Component_numbers<<endl;
	 delete [] visited;//結束后刪除數(shù)組	
}
//從名為 v 的頂點出發(fā)廣度優(yōu)先遍歷
template <class T, class E> 
void Graphlnk<T,E>::BFS(const T& v)
{    int i, w; 
      //創(chuàng)建訪問標記數(shù)組
     bool* visited = new bool[this->numVertices];
      //對圖中所有頂點初始化訪問數(shù)組visited
     for (i = 0; i < this->numVertices; i++)
          visited[i] = false; //初始化為都未訪問過
    int loc = getVertexPos (v); //取名為 v 的頂點序號
    if(loc == -1) //名為 v 的頂點未找到
        cout<<"頂點 "<<v<<"沒有找到,廣度優(yōu)先遍歷失敗。";
     else
     {
        cout << getValue (loc) << ' '; //訪問頂點 v
        visited[loc] = true; //標記頂點 v 已訪問過
        //頂點 v 進隊列, 實現(xiàn)分層訪問
        queue<int> q; 
		 q.push(loc); //訪問過的頂點進隊列 
        while (!q.empty() ) //循環(huán), 訪問所有結點
        {	loc = q.front();//記錄當前隊列第一個頂點的值 
             q.pop(); // 然后記錄完讓它出隊列 
             w = getFirstNeighbor (loc); //取它的第一個鄰接頂點 
            while (w != -1)  //當鄰接頂點w存在
            {   if (!visited[w]) //如果未訪問過
                 {   cout << getValue (w) << ' ';//訪問它然后輸出它 
                      visited[w] = true;	//標記此頂點已經(jīng)訪問過
                      q.push(w); 	//頂點w進隊列
                 }
                 w = getNextNeighbor (loc, w); //取下一個鄰接頂點 
            }
            
        } //外層循環(huán),判隊列空否
    }
    delete [] visited;
}

template<class T , class E >
void Graphlnk<T,E>::DFS(const T &v)
{
	int sign;
	bool *visited = new bool[this->numVertices];
	for(int i = 0; i <this->numVertices;i++)
	{
		visited[i]=false;//初始化都為為訪問過 
	}
	sign=this->getVertexPos(v);
	if(sign==-1)
	cout<<"頂點"<<v<<"不存在"<<"深度優(yōu)先遍歷失敗"<<endl;
	else //否則為存在
	DFS(sign,visited);
	delete[] visited; //深度優(yōu)先遍歷完成的話,就刪除這個數(shù)組 
}
template<class T,class E>
void Graphlnk<T,E>::DFS(int v , bool visited[])
{
	cout<<this->getValue(v)<<" ";
	visited[v]=true;
	int w = this->getFirstNeighbor(v);
	while(w!=-1)
	{
		if(!visited[w])//如果沒有訪問過為真 
		DFS(w,visited);
		w=this->getNextNeighbor(v,w);//否則回退 然后繼續(xù)搜索 
	}
}


template<class T,class E>
bool Graphlnk<T,E>::removeEdge (int v1, int v2)//刪除邊這個比較簡單 
{	
		if(v1<0||v2<0||v1>this->numVertices||v2>this->numVertices)//這種情況就是不符合,不符合就返回false  
		return false ;
		else //否則為符合的 
		{
		
	Edge_Vertices<T,E> *temp;//這是一個中間變量 
	temp=this->NodeTable[v1].next;//讓它先等于頂點表個的下一個 
	while(temp)
	{		
		if(temp->dest==v2)//如果一開始就是等于要刪除的那個頂點的話(可能會有誤解哦,這里不是刪除邊么,怎么刪除頂點?)
		//因為刪除末頂點的話,就會少掉一條邊 
		{
			if(temp->link==NULL)//如果這條鏈只有這個待刪除的頂點 
			{
				this->NodeTable[v1].next=NULL;//讓頂點表的那個頂點的下一個頂點指向空 
				delete temp;	//刪除這個頂點 
				break;//跳出循環(huán) 
			}
			else{ //如果不為空的話 
		 	this->NodeTable[v1].next = temp->link;//讓頂點表的下一個頂點指向 待刪除 目標頂點的下一個 
		 	delete temp;//刪除結點 
		 	break;
		 }
		}	if(temp)//防止內存訪問出錯 
		if(temp->link->dest ==v2)//不是第一個頂點是要刪除的頂點  這個if 語句記作 AAAAA  
		{	Edge_Vertices<T,E> *temp2;//定義一個中間變量temp2 
			temp2=temp->link;//讓temp  2指向 待刪除 目標定點 
			temp->link=temp->link->link;//利用temp 斷開 目標定點 與 子鏈的連接 
			delete temp2;//刪除目標頂點 
			break;//跳出循環(huán) 
		}	if(temp)//防止內存訪問出錯 
	 		temp=temp->link;//根據(jù)這條語句,會回到剛才 AAAAA 那個 if語句 
	}
	//要考慮有向圖和無向圖哦
	if(this->direction==false)//如果是無向圖的話 
	{
	     temp=this->NodeTable[v2].next;
	while(temp)
	{			//此段代碼與上面的邏輯完全相同,不再贅述 
		if(temp->dest==v1)
		{
			if(temp->link==NULL)
			{
				this->NodeTable[v2].next=NULL;
				delete temp;
				break;	
			}
			else{ 
		 	this->NodeTable[v2].next = temp->link;
		 	delete temp;
		 	break;
		 }
		}
		if(temp)
		if(temp->link->dest ==v1)
		{	Edge_Vertices<T,E> *temp2;
			temp2=temp->link;
			temp->link=temp->link->link;
			delete temp2;
			break;
		}	if(temp)
	 		temp=temp->link;
	}
	 
   }
}
this->numEdges--;//記得刪完 邊數(shù)要 -1; 
return true;//刪除成功返回true  
}


template<class T , class E >
bool Graphlnk<T,E>::removeVertex (int v) //刪除序號為 v 的頂點
{
    if(v<0||v>=this->numVertices)//如果下標不符合規(guī)定 
	return false;
	else //否則為符合 
	{
	
	int del_vex_num=v;//記錄這個刪除的下標 
	if(v!=this->numVertices-1)//如果不是刪除最后一個頂點的話 
	this->NodeTable[v]=this->NodeTable[--this->numVertices];//就用最后一個頂點頂?shù)哪菞l"鏈"替代這個待刪除頂點的"鏈" 
	//如果刪除的是最后一個頂點的話,自然就沒有影響了 
	else 
{
		this->numVertices--;
	int end_edg=0;//因為如果直接刪掉這個點的話,那么這個點關聯(lián)的邊也要減掉
	Edge_Vertices<T,E> *p;
	p=this->NodeTable[v].next;
	while(p)
	{	
		if(p)
		{  end_edg++;//如果有一個頂點,且不為空的話,那么涉及的邊數(shù)就+1 
		 } 
		 p=p->link;
	 } 
	 this->numEdges-=end_edg;
}
	for(int i= 0 ; i < this->numVertices;i++)//此時的numVertices已經(jīng)-1 ,然后對這所有的鏈進行操作,如果有將要刪除頂點與這個頂點相連,則刪除 
	{	Edge_Vertices<T,E> *temp;
		temp=this->NodeTable[i].next;
		//第一種情況:第一個連接的 頂點就是 將要刪除的那個頂點
		//這種情況很好做
		if(temp->dest ==v)
		{
			if(temp->link==NULL)//如果這條鏈只有一個結點,然后第一個結點恰好是這個將要刪除的頂點的話 
			{ 
				this->NodeTable[i].next=NULL;//這樣的話,直接刪掉它	
				this->numEdges--;
			}
			else //否則
			 {
			 this->NodeTable[i].next=temp->link;
			 delete temp;//刪除臨時的指針變量 
			 this->numEdges--;
			}
		 } 
		 else //第二種情況,就需要循環(huán)了,因為每一個頂點最多只有一個 將要刪除的那個頂點的下標 
		 {//第一個結點的dest!=v 
		 	while(temp)
			 {
			 	if(temp->link)
				 {
				 
			 	if(temp->link->dest==v)//第一個的下一個頂點剛好是 這個dest 的話  
			 	{
			 		Edge_Vertices<T,E> *temp2;
			 		temp2=temp->link;//中間變量temp2; 
			 	  	temp->link=temp->link->link;//斷開這個將要刪除的頂點
					delete temp2;//刪除剛才的中間變量// 
					this->numEdges--;
					break;
				 } 
				}
				 temp=temp->link;//這個最終會變成NULL,最后跳出循環(huán); 
			  } 
		 }
	}
	if(v!=this->numVertices)//如果這個待刪除的下標不等于 最后那個頂點的話 ,因為頂點 調動 位置,所以需要改變 鏈中頂點下標名字 
	for(int i = 0 ; i < this->numVertices;i++)
	{		Edge_Vertices<T,E> *temp;
		temp=this->NodeTable[i].next;
		while(temp)
		{  
			if(temp)
			if(temp->dest==this->numVertices)//就是說,最后的那個頂點要去前面 替換掉 剛才刪除的那個頂點的位置 ,因此,下標也要改 
			{
				temp->dest=v;// 循環(huán)遍歷 出最后頂點的dest  然后用v  替換  
				break;
			}
			temp=temp->link;//移動 
		}
	}
	}//第一個else 的右括號 
	return true;//刪除頂點成功 
}
template<class T , class E>
	E Graphlnk<T,E>::getWeight (int v1, int v2)//獲得兩個點之間的權值 
	{
		if(v1<this->numVertices&&v2<this->numVertices&&v1!=v2)
		{  
	//		 string a = this->NodeTable[v2].data;
			 Edge_Vertices<T,E> *temp;
			 temp=this->NodeTable[v1].next;//從v1這條鏈找一個點開始 
			while(temp)//然后循環(huán),直到找到v2 
			{
			  if(temp->dest==v2)//找到了直接返回它的權值 
			  return temp->cost;
			  else 
			  temp=temp->link;	//沒找到,移動 
			}
			return maxWeight;
			
		}
	}




template<class T , class E >
bool Graphlnk<T,E>::insertVertex (const T& vertex)//插入頂點 ,插在之前定義的那個頂點表那里 
{
	if(this->numVertices<this->maxVertices)//如果圖當前的頂點數(shù)小于 允許插入的最大頂點數(shù),則可以插入 
	{
		this->NodeTable[this->numVertices++].data=vertex;
		
	}
}
template<typename T, typename E>
bool Graphlnk<T,E>::insertEdge(T in, T out, E cost)//插入邊 
{   int  v1= getVertexPos(in); //這里還是直接是輸入定點名,用函數(shù)找這個頂點的下標 
    int v2=getVertexPos(out);
	if(v1>-1 && v1<this->numVertices && v2>-1 && v2 < this->numVertices )//這兩個下標都在頂點表里
	{     //將新邊的權值插入邊鄰接矩陣的第v1行,v2列,利用頭插法
	     Edge_Vertices<T,E> *temp =new Edge_Vertices<T,E>; //生成一個邊結點。
		 temp->dest=v2;// 記錄這個點的值 
		 temp->link=this->NodeTable[v1].next;//將它插在 v1 這個頂點的這條鏈里 ,這里采用頭插法 (temp 街上NodeTable[v1]的后面 一大串(當然一開始為空))
		 //比如這一大串為ABCDE  然后temp  接上去就為 temp->ABCDE; 
		 if(this->withCost==true)//如果需要記錄權值 
		 temp->cost=cost;// 記錄 
		 this->NodeTable[v1].next =temp;// 吧temp接上去 head ->temp->ABCDE;
		 
		 
		 if(this->direction==false)//如果是無向圖的話,還要從v2那條鏈 接上 v1  
		  {
		  Edge_Vertices<T,E> *temp2=new Edge_Vertices<T,E> ;
		  temp2->dest=v1;
		  temp2->link=this->NodeTable[v2].next;
			if(this->withCost==true)
			 temp2->cost=cost;//同樣的是采用頭插法,不再一一贅述 
			this->NodeTable[v2].next=temp2;
		} 
		this->numEdges++;
	return true;
	}
	else return false; //插入新邊失敗(不滿足if 語句)
}
//構造函數(shù)建立鄰接表
template <class T, class E>
Graphlnk<T, E>::Graphlnk (int sz,bool direct, bool cost):Graph<T,E>(sz, direct, cost)
{	//創(chuàng)建源點表數(shù)組
   NodeTable = new Vertex<T, E>[this->maxVertices];//分10個Vertex結點大小 的指針 數(shù)組 
    if (NodeTable == NULL) 
    {
	 cerr << "內存分配出錯!" << endl;  exit(1); 
	}
    for (int i = 0; i < this->maxVertices; i++)  
        NodeTable[i].next= NULL;//對NodeTable的指針進行初始化 
}
//析構函數(shù):刪除一個鄰接表
template <class T, class E>
Graphlnk<T, E>::~Graphlnk()
 {	
   for (int vertex = 0; vertex < this->numVertices; vertex++ ){
        //current指向源點vertex邊鏈表的第1個鄰接點
	    Edge_Vertices<T, E> * current = NodeTable[vertex].next;
        while (current != NULL) {//鄰接點存在
            NodeTable[vertex].next = current->link; //脫鏈
            delete current; //釋放邊鏈表的第1個鄰接點
            // current重新指向邊鏈表的第1個鄰接點
            current = NodeTable[vertex].next;
        }
   	} 
   	delete []NodeTable; //刪除源點表數(shù)組
}
//返回序號為 v 的源點第1個鄰接點的序號(從0開始), 
//若未找到鄰接點, 則返回-1
template <class T, class E>
int Graphlnk<T, E>::getFirstNeighbor (int v)
{   if (v != -1) //源點v存在
	 {  //current指向源點v的邊鏈表第1個鄰接點
         Edge_Vertices<T, E>* current = NodeTable[v].next;
	     if (current != NULL)//頂點v的第1個鄰接點存在
            //返回第1個鄰接點的序號
            return current ->dest;
   	}
   	return -1; //不存在第1個鄰接點,返回-1
}
//返回源點v和鄰接點w的下1個鄰接點的序號,
//若未找到下1個鄰接點, 則返回-1
template <class T, class E>
int Graphlnk<T, E>::getNextNeighbor (int v, int w)
{  if (v != -1) { //源頂點 v 存在
        Edge_Vertices<T, E>* current = NodeTable[v].next;//終頂點current 
        while (current != NULL && current->dest != w)
             current = current->link; //先找到終頂點 w
	    if (current != NULL && current->link != NULL) 
	        //返回w的下1個鄰接頂點序號
            return current->link->dest;
   	}
   	return -1; //未找到下1個鄰接頂點,返回-1
}
template <class T, class E>
void Graphlnk<T,E>::Enter()
{	int Count,vertexs,Edges;
	T e1,e2;
	cout<<"圖中共有多少個頂點?";
	cin>>vertexs;
	cout<<"輸入圖中共 "<<vertexs<<" 個頂點名:";
    //輸入圖中全部頂點名
	for(Count=0;Count<vertexs;Count++)
	{	cin>>e1;
		insertVertex(e1);
	}
	E weight;
	char Answer;
	cout<<"圖形的邊有方向嗎(y/n)?";
	cin>>Answer;
	if(Answer=='y' || Answer=='Y')
		this->direction=true;
	else
		this->direction=false;
cout<<"圖中的邊帶權嗎(y/n)?";
	cin>>Answer;
	if(Answer=='y' || Answer=='Y')
		this->withCost=true;
	else
		this->withCost=false;
	cout<<"圖中共有多少條邊?";
	cin>>Edges;
	Count=0;
while(Count<Edges)
	{
		cout<<"輸入第 "<<Count+1<<" 條邊的2個頂點名和權值:";
		cin>>e1>>e2>>weight;
		if(insertEdge(e1,e2,weight)) 
		      Count++;
		else
                cout<<"頂點名有誤,重新輸入這條邊!"<<endl;
	}
}
// 輸出圖中所有頂點和邊的信息
template <class T, class E>
void Graphlnk<T, E>::Print(void)
{	int row,column;
	E weight;
	cout<<"圖中共有 "<<this->numVertices<<" 個頂點和 "<<this->numEdges<<" 條邊:"<<endl;

	
for(row=0;row<this->numVertices;row++)
	{
		//按行號取出序號為row的頂點名并輸出
		cout<<"序號"<<row<<"源點"<<"["<<getValue(row)<<"]"<<"->";
		Edge_Vertices<T, E> *temp;
		temp=this->NodeTable[row].next;
		if(temp)//可能它的下一個頂點直接就是空 
		{
		
		while(temp->link)
		{
			cout<<"["<<"鄰接點"<<temp->dest<<"]";
			if(this->withCost)
			cout<<"["<<"權值"<<temp->cost<<"]";
			cout<<"[-]->" ;
		temp=temp->link;
		
		}
		cout<<"["<<"鄰接點"<<temp->dest<<"]";
		if(this->withCost)
			cout<<"["<<"權值"<<temp->cost<<"]";
			cout<<"[^]" ;
	cout<<endl;
		}
		else 
		cout<<"[^]"<<endl;
}

}

int main()
{	
	Graphlnk<string,double> graph;
	graph.Enter();
	graph.Print();
//	string del;
//	cout<<"請輸入你想要刪除的一個頂點"<<endl;
//	cin>>del;
// 	int index= graph.getVertexPos(del);
//	if(graph.removeVertex (index))
//	cout<<"刪除成功"<<endl;
//	else 
//	cout<<"刪除失敗";
//	graph.Print();
//	cout<<"請輸入你想要刪除那條邊的兩個頂點"<<endl;
//	string one ,two;
//	cin>>one>>two;
//	int num_one,num_two;
//	num_one=graph.getVertexPos(one);
//	num_two=graph.getVertexPos(two);
//	if(graph.removeEdge(num_one,num_two))
//	cout<<"刪除成功"<<endl;
//	else 
//	cout<<"刪除失敗"<<endl; 
//	graph.Print();
//cout<<"請輸入一個頂點來進行深度優(yōu)先遍歷"<<endl;
// string dfs;
// cin>>dfs;
// graph.DFS(dfs);
// cout<<endl; 
//cout<<"請輸入一個頂點來進行廣度優(yōu)先遍歷"<<endl;
//string bfs;
//cin>>bfs;
//graph.BFS(bfs);
//cout<<endl;
//graph.Connected_Component();
	string  a,b;
	cout<<"輸入求最短路徑的始頂點:";cin>>a;
	int v = graph.getVertexPos(a);
	cout<<"輸入求最短路徑的終頂點:";cin>>b;
	int w = graph.getVertexPos(b);
	graph.Dijkstra(v);
	graph.Print(v,w);
//system("PAUSE");
}

運行結果如下:

以上就是C++ Dijkstra算法之求圖中任意兩頂點的最短路徑的詳細內容,更多關于C++ 的資料請關注腳本之家其它相關文章!

相關文章

  • 詳解C++設計模式編程中對訪問者模式的運用

    詳解C++設計模式編程中對訪問者模式的運用

    這篇文章主要介紹了C++設計模式編程中對訪問者模式的運用,訪問者模式在不破壞類的前提下為類提供增加新的新操作,需要的朋友可以參考下
    2016-03-03
  • C++?Boost?ProgramOptions超詳細講解

    C++?Boost?ProgramOptions超詳細講解

    Boost是為C++語言標準庫提供擴展的一些C++程序庫的總稱。Boost庫是一個可移植、提供源代碼的C++庫,作為標準庫的后備,是C++標準化進程的開發(fā)引擎之一,是為C++語言標準庫提供擴展的一些C++程序庫的總稱
    2022-11-11
  • C++?socket通信遇到的問題及解決方法

    C++?socket通信遇到的問題及解決方法

    這篇文章主要介紹了C++?socket通信遇到的問題,通過代碼修改來解決這個問題,本文結合實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2023-08-08
  • C語言詳細講解#error與#line如何使用

    C語言詳細講解#error與#line如何使用

    這篇文章主要介紹了C語言中#error與#line如何使用,#error與#line雖然在語言里面用的比較少,但是還是有必要了解一下
    2022-04-04
  • C++事件驅動型銀行排隊模擬

    C++事件驅動型銀行排隊模擬

    這篇文章主要為大家詳細介紹了C++事件驅動型銀行排隊模擬,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • C++ qt 使用jsoncpp json 讀寫操作

    C++ qt 使用jsoncpp json 讀寫操作

    JsonCpp是一個基于C++語言的開源庫,用于C++程序的Json數(shù)據(jù)的讀寫操作,本文重點給大家介紹C++ qt 使用jsoncpp json 讀寫操作,感興趣的朋友跟隨小編一起看看吧
    2021-11-11
  • QT實現(xiàn)簡單TCP通信

    QT實現(xiàn)簡單TCP通信

    這篇文章主要為大家詳細介紹了QT實現(xiàn)簡單的TCP通信,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • C++中STL的優(yōu)先隊列priority_queue詳解

    C++中STL的優(yōu)先隊列priority_queue詳解

    這篇文章主要介紹了C++中STL的優(yōu)先隊列priority_queue詳解,今天講一講優(yōu)先隊列(priority_queue),實際上,它的本質就是一個heap,我從STL中扒出了它的實現(xiàn)代碼,需要的朋友可以參考下
    2023-08-08
  • C語言實現(xiàn)雙人五子棋游戲

    C語言實現(xiàn)雙人五子棋游戲

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)雙人五子棋游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • C語言 位域詳解及示例代碼

    C語言 位域詳解及示例代碼

    本文主要介紹C語言 位域的知識,這里整理了相關資料,并附示例代碼及詳解,有興趣的小伙伴可以參考下
    2016-08-08

最新評論