【資料結構與演算法】 有向圖的最短路徑實現
Goal: Practice the algorithms of shortest pathproblem.
Task:用一個有向圖表示給定的n個(要求至少10個)城市(或校園中的一些地點)及其之間的道路、距離情況,道路是有方向的。要求完成功能:根據使用者輸入的任意兩個城市,給出這兩個城市之間的最短距離及其路徑。
要求使用真實地點及其位置,可以使用百度地圖獲得各點座標,或者獲取可達的兩點間距離。
採用Dijkstra演算法
Dijkstra演算法是由荷蘭電腦科學家艾茲格·迪科斯徹發現的。演算法解決的是有向圖中最短路徑問題。
舉例來說,如果圖中的頂點表示城市,而邊上的權重表示著城市間開車行經的距離。 Dijkstra演算法可以用來找到兩個城市之間的最短路徑。
Dijkstra演算法的輸入包含了一個有權重的有向圖G,以及G中的一個來源頂點S。 我們以V表示G中所有頂點的集合。圖中的每一個邊,都是兩個頂點所形成的有序元素對。(u,v)表示從頂點u到v有路徑相連。 假設E為所有邊的集合,而邊的權重則由權重函式w: E → [0, ∞]定義。 因此,w(u,v)就是從頂點u到頂點v的非負花費值(cost)。 邊的花費可以想像成兩個頂點之間的距離。任兩點間路徑的花費值,就是該路徑上所有邊的花費值總和。 已知有V中有頂點s及t,Dijkstra演算法可以找到s到t的最低花費路徑(i.e. 最短路徑)。 這個演算法也可以在一個圖中,找到從一個頂點s到任何其他頂點的最短路徑。
採用不同顏色標註配圖路線防止混淆
想到了離散數學的哈密頓圖
#include <windows.h>
#include <stdlib.h>
#include <conio.h>
using namespace std;
int arrow(int up,int down);
void title();
void gotoxy(int xpos, int ypos);
void frontPage();
int main();
void search_path();
void quit();
void find_the_path(const int start, const int dest, int& shortest, int * path);
int minVertex(int* D);
void print_path(int * path, int dest, int start);
class Graph
{
public:
virtual int verNum() = 0;
virtual int edgNum() = 0;
virtual int firstNghbr(int v) = 0;
virtual int nextNghbr(int v1, int v2) = 0;
virtual void setEdge(int v1, int v2, int wgt) = 0;
virtual void delEdge(int v1, int v2) = 0;
virtual int weight(int v1, int v2) = 0;
virtual int getMark(int v) = 0;
virtual void setMark(int v, int val) = 0;
};
class Graphm : public Graph
{
private:
int numVertex, numEdge;
int **matrix;
int *mark;
public:
Graphm()
{}
Graphm(int numVert) //宣告一張圖(用鄰接矩陣方法實現)
{
int i,j;
numVertex = numVert;
numEdge = 0;
mark = new int[numVert];
for(i=0; i<numVertex; i++)
mark[i] = 0; //初始化各點訪問情況為0,即未訪問
matrix = (int**) new int*[numVertex];
for(i=0; i<numVert; i++)
matrix[i] = new int[numVertex];
for(i=0; i<numVertex; i++)
for(j=0; j<numVertex; j++)
matrix[i][j] = 0; //權為0表示兩點間沒有邊連線
}
~Graphm()
{
delete [] mark;
int i;
for(i=0; i<numVertex; i++)
delete [] matrix[i];
delete [] matrix;
}
int verNum()
{
return numVertex;
}
int edgNum()
{
return numEdge;
}
int firstNghbr(int v)
{
int i;
for(i=0; i<numVertex; i++)
if(matrix[v][i] != 0)
return i;
return i; //若無第一個鄰接結點返回節點數
}
int nextNghbr(int v1, int v2)
{
int i;
for(i=v2+1; i<numVertex; i++)
if(matrix[v1][i] != 0)
return i;
return i;
}
void setEdge(int v1, int v2, int wgt)
{
if(matrix[v1][v2] == 0)
numEdge++;
matrix[v1][v2] = wgt;
}
void delEdge(int v1, int v2)
{
if(matrix[v1][v2] != 0)
numEdge--;
matrix[v1][v2] = 0;
}
int weight(int v1, int v2)
{
return matrix[v1][v2];
}
int getMark(int v)
{
return mark[v];
}
void setMark(int v, int val)
{
mark[v] = val;
}
};
class City
{
private:
int id;
char * cityName;
public:
City()
{}
City(int newId, char* newName)
{
id = newId;
cityName = newName;
}
char* getCityName()
{
return cityName;
}
};
City* cityDB = new City[10]; //宣告十個城市
Graphm cityGraph = Graphm(10); //宣告城市間火車路線圖
void gotoxy(int xpos, int ypos)
{
COORD scrn;
HANDLE hOuput = GetStdHandle(STD_OUTPUT_HANDLE);
scrn.X = xpos; scrn.Y = ypos;
SetConsoleCursorPosition(hOuput,scrn);
}
void title()
{
gotoxy(0,0);
cout<<"**********************************************************************"<<endl;
cout<<endl;
cout<<" 歡迎使用城市間最短里程路徑查詢程式 "<<endl;
cout<<endl;
cout<<"**********************************************************************"<<endl;
}
int arrow(int up,int down)
{
int i,j,c;
gotoxy(20,up);
j=up;
cout<<"-->";
for(i=0;i<10000;i++)
{
if(j>down)
{
gotoxy(20,j);
cout<<" ";
j=up;
gotoxy(20,j);
cout<<"-->";
}
if(j<up)
{
gotoxy(20,j);
cout<<" ";
j=down;
gotoxy(20,j);
cout<<"-->";
}
c=getch();
if(c==10 || c==13)
break;
if(c==224)
{
c=getch();
if(c == 80)
{
gotoxy(20,j+1);
cout<<"-->";
gotoxy(20,j);
cout<<" ";
j++;
}
if(c == 72)
{
gotoxy(20,j-1);
cout<<"-->";
gotoxy(20,j);
cout<<" ";
j--;
}
}
}
return j;
} //游標移動選擇功能
void frontPage()
{
system("cls");
title();
cout<<endl;
cout<<endl;
cout<<endl;
cout<<endl;
cout<<" 請上下鍵選擇操作:\n"<<endl;
cout<<endl;
cout<<endl;
cout<<" 查詢城市間最短里程路徑 "<<endl;
cout<<" 退出程式 "<<endl;
int j = arrow(13,14);
system("cls");
switch (j)
{
case 13:search_path();break;
case 14:quit();break;
}
}
void quit()
{
system("cls");
exit(0);
}
void search_path()
{
title();
cout<<endl;
int start=11, dest=11;
char start1,dest1;
cout<<"本程式儲存有以下十個城市的線路資訊,請輸入其中任意兩個城市ID獲取之間最短路徑: "<<endl;
cout<<endl;
cout<<" 0.北京 1.天津 2.上海 3.武漢 4.廣州"<<endl;
cout<<" 5.南京 6.重慶 7.成都 8.西安 9.鄭州"<<endl;
cout<<endl;
cout<<"提示:若輸入的ID不在[0,9]範圍內或兩次輸入同樣的ID,輸入介面會重新整理等待重新輸入";
while(start<0||start>=10||dest<0||dest>=10||start==dest)
{
gotoxy(17,13);
cout<<"輸入出發城市ID: ";
gotoxy(17,14);
cout<<"輸入目的城市ID: ";
gotoxy(33,13);
cin>>start1;
start=int(start1);
if(start1=='0')
start=10;
else if(start1=='1')
start=1;
else if(start1=='2')
start=2;
else if(start1=='3')
start=3;
else if(start1=='4')
start=4;
else if(start1=='5')
start=5;
else if(start1=='6')
start=6;
else if(start1=='7')
start=7;
else if(start1=='8')
start=8;
else if(start1=='9')
start=9;
gotoxy(33,14);
cin>>dest1;
dest=int(dest1);
if(dest1=='0')
dest=10;
else if(dest1=='1')
dest=1;
else if(dest1=='2')
dest=2;
else if(dest1=='3')
dest=3;
else if(dest1=='4')
dest=4;
else if(dest1=='5')
dest=5;
else if(dest1=='6')
dest=6;
else if(dest1=='7')
dest=7;
else if(dest1=='8')
dest=8;
else if(dest1=='9')
dest=9;
}
int shortest = 100000; //儲存最短距離
int path[10]; //儲存最短路徑
int i;
for(i=0;i<10;i++)
path[i] = 11;
find_the_path(start,dest,shortest,path);
cout<<endl;
cout<<cityDB[start].getCityName()<<" 到 "<<cityDB[dest].getCityName()<<" 的最短里程為 "<<shortest<<" 公里, 路徑為:"<<endl;
cout<<cityDB[start].getCityName()<<"-->";
print_path(path,dest,start);
cout<<cityDB[dest].getCityName()<<endl;
cout<<endl;
cout<<"按任意鍵返回主介面"<<endl;
system("pause");
for(i=0;i<10;i++)
cityGraph.setMark(i,0); //重置訪問標記
frontPage();
}
void print_path(int * path, int dest, int start)
{
int k;
k = path[dest];
if(k == start)
return;
print_path(path,k,start);
cout<<cityDB[k].getCityName()<<"-->";
}
void find_the_path(const int start, const int dest, int& shortest, int * path) //Dijkstra演算法應用
{
int D[10]; //儲存設定的源城市到任意城市的估計最小長度
int i,v,w;
for(i=0; i<10; i++)
{
D[i] = 100000;
path[i] = -1;
}
D[start] = 0;
//path[start] = 0;
for(i=0; i<cityGraph.verNum(); i++)
{
v=minVertex(D);
if(D[v] == 100000)
return;
cityGraph.setMark(v,1);
for(w=cityGraph.firstNghbr(v); w<cityGraph.verNum(); w=cityGraph.nextNghbr(v,w))
{
if(D[w]>(D[v]+cityGraph.weight(v,w)))
{
D[w] = D[v] + cityGraph.weight(v,w);
path[w] = v;
}
}
}
shortest = D[dest];
}
int minVertex(int* D)
{
int i,v;
for(i=0; i<cityGraph.verNum(); i++)
{
if(cityGraph.getMark(i) == 0)
{
v = i;
break;
}
}
for(i++; i<cityGraph.verNum(); i++)
if((cityGraph.getMark(i) == 0) && (D[i] < D[v]))
v = i;
return v;
}
int main()
{
system("mode con cols=70 lines=23");
char c[20]="color 7f";
system(c); //對cmd視窗顯示的調整變化(美化)
cityDB[0] = City(0,"北京");
cityDB[1] = City(1,"天津");
cityDB[2] = City(2,"上海");
cityDB[3] = City(3,"武漢");
cityDB[4] = City(4,"廣州");
cityDB[5] = City(5,"南京");
cityDB[6] = City(6,"重慶");
cityDB[7] = City(7,"成都");
cityDB[8] = City(8,"西安");
cityDB[9] = City(9,"鄭州");
cityGraph.setEdge(1,0,108);
cityGraph.setEdge(2,1,956);
cityGraph.setEdge(0,9,624);
cityGraph.setEdge(2,9,807);
cityGraph.setEdge(9,8,427);
cityGraph.setEdge(9,3,457);
cityGraph.setEdge(8,3,661);
cityGraph.setEdge(7,8,617);
cityGraph.setEdge(6,7,267);
cityGraph.setEdge(3,6,752);
cityGraph.setEdge(3,2,686);
cityGraph.setEdge(3,5,457);
cityGraph.setEdge(5,6,1199);
cityGraph.setEdge(2,4,1219);
cityGraph.setEdge(4,3,846);
cityGraph.setEdge(4,5,1142);
frontPage();
return 0;
}
能否用同一個呼叫形式,既能呼叫派生類又能呼叫基類的同名函式。
C++中虛擬函式的作用是允許在派生類中重新定義與基類同名的函式,並且可以通過基類指標或引用來訪問基類和派生類中的同名函式。
純虛擬函式在基類中是沒有定義的,必須在子類中加以實現,很像java中的介面函式!
百度解釋:
在某基類中宣告為 virtual 並在一個或多個派生類中被重新定義的成員函式,用法格式為:virtual 函式返回型別 函式名(引數表) {函式體};實現多型性,通過指向派生類的基類指標或引用,訪問派生類中同名覆蓋成員函式- 虛擬函式
- virtual function
- 被virtual關鍵字修飾的成員函式