資料結構與演算法——無權最短路徑演算法的C++實現
對於一個有權圖,任意路徑中各個邊的權重的和,就是加權路徑長。
對於一個無權圖,任意路徑中邊的數目,就是無權路徑長。
對於上面的無權圖G,我們使用某個頂點s作為輸入引數,我們想要找出從s到所有其它頂點的最短路徑。我們只對路徑上邊的數目感興趣,不考慮路徑上邊的權重(對於無權圖可以將權重看為是1)。
演算法1詳細步驟:
1、選擇頂點s為v3。馬上可以知道s到v3的最短路徑長為0的路徑(v3->v3)
2、尋找從s出發路徑長為1的頂點,這些點都是s的鄰接點.
3、尋找從s出發路徑長為2的頂點。找到v1和v6的鄰接點(距離v1和v6為1),那麼這些點距離s為2。
4、尋找從s出發路徑長為3的頂點。這也是最後的路徑長度。
這種搜尋圖的方法稱為廣度優先搜尋。該方法按層處理頂點:距離開始點最近的那些頂點首先被求值,而最遠的那些頂點最後被求值。這很像對樹的層序遍歷。
對於每個頂點,我們將跟蹤三個資訊。首先,把從s開始到頂點的距離放到dv欄中。開始的時候,除了s外的其它頂點都是不可達的,而到s的路徑長為0。pv記錄的是該頂點之前的一個頂點。known表示該頂點是否被處理過(確認了距離),初始值都是false,如果確定了s到該頂點的距離,則將known置為true。
儲存各個頂點資訊的資料結構(也是上面配置表中的結構):
//儲存每個頂點資訊的資料結構
struct GraphNode{
bool known;//當前頂點距離起點的距離是否確定
int dist;//當前頂點到起點的最短距離
int path;//當前頂點距離起點的最短路徑的前一個頂點
};
圖類的介面:
/*******************************************************
* 類名稱: 鄰接表圖
********************************************************/
class Graph{
private:
int edge_num;//圖邊的個數
int vertex_num;//圖的頂點數目
list<Node> * graph_list;//鄰接表
vector<GraphNode> nodeArr;//儲存每個頂點資訊的陣列
public:
Graph(){}
Graph(char* graph[], int edgenum);
~Graph();
void print();
void unwightedShorestPath(int src);//演算法1求最短距離
void unwightedShorestPathAdv(int src);//演算法2求最短距離
void printShorestPath(); //輸出頂點src到各個頂點最短距離的資訊
private:
vector<int> get_graph_value(char* graph[], int columns);
void addEdge(char* graph[], int columns);
};
關於圖鄰接表表示法的實現參考:資料結構與演算法——圖的鄰接表表示法類的C++實現
演算法1具體步驟:
1、初始化上面的配置表,known欄全部設為false,dv欄全部設定為無窮大,pv欄初始化為0.
2、先把距離為0上的頂點的dist設為0。
3、從距離currentDist為0開始,遍歷每個頂點,如果找到某個頂點的known為false並且該頂點的dv==currentDist,則將該頂點的known設定為true。然後再遍歷所有與該頂點相鄰的頂點,如果這些相鄰的頂點的dv是無窮大,則將dv設定為currentDist+1,並在這些相鄰頂點的pv欄位記錄該頂點。
4、將currentDist++。重複步驟3,直到currentDist等於頂點的數目。
演算法1的函式實現:
/*************************************************
* 函式名稱:unwightedShorestPath(int src)
* 功能描述:求無權圖的任意點到其它頂點的距離
* 引數列表:src是起點
* 返回結果:void
*************************************************/
void Graph::unwightedShorestPath(int src)
{
//步驟1,初始化配置表
for(int i = 0; i < vertex_num; ++i){
nodeArr[i].known = false;
nodeArr[i].dist = INFINITY;
nodeArr[i].path = 0;
}
//步驟2,先把距離為0的頂點的dist設定為0
nodeArr[src].dist = 0;
//步驟4
for(int currentDist = 0; currentDist < vertex_num; ++currentDist){
//步驟3
for(int i = 0; i < vertex_num; ++i){
if((!nodeArr[i].known) && (nodeArr[i].dist == currentDist)){
nodeArr[i].known = true;
//遍歷與頂點i相鄰的所有頂點
for(list<Node>::iterator it = graph_list[i].begin(); it != graph_list[i].end(); ++it){
if(nodeArr[(*it).vertex].dist == INFINITY){
nodeArr[(*it).vertex].dist = currentDist + 1;
nodeArr[(*it).vertex].path = i;
}
}
}
}
}
}
在演算法2中將已經確定了的頂點,還有沒有確定的頂點分開。使用了一個佇列來實現,放入佇列中的都是沒有確定的,從佇列中彈出的頂點都是已經確定的。
演算法2具體步驟:
1、將配置表中各個頂點的dist欄位進行初始化為無窮大。
2、將頂點s的dist欄位設為0。
3、將頂點s壓入佇列que中。
4、如果佇列que非空,則將隊首的元素v彈出。並遍歷與v相鄰的所有頂點w,如果頂點w的dist為無窮大,則將w.dist設為v.dist+1;w.path=v;並將頂點w壓入佇列。
5、迴圈執行步驟4,直至佇列que為空。
該演算法2沒有使用known欄位。
演算法2函式實現:
/*************************************************
* 函式名稱:unwightedShorestPathAdv(int src)
* 功能描述:求無權圖的任意點到其它頂點的距離,
* 該演算法比unwightedShorestPathAdv要好
* 引數列表:src是起點
* 返回結果:void
*************************************************/
void Graph::unwightedShorestPathAdv(int src)
{
queue<int> que;
//步驟1,將各個頂點的dist設定為無窮大
for(int i = 0; i < vertex_num; ++i)
nodeArr[i].dist = INFINITY;
//步驟2,將頂點src的dist欄位設為0
nodeArr[src].dist = 0;
//步驟3,將頂點src壓入佇列que中
que.push(src);
//步驟5
while(!que.empty()){
//步驟4
int top = que.front();//獲得佇列的首元素
que.pop();//彈出佇列的首元素
//遍歷與頂點相鄰的所有頂點
for(list<Node>::iterator it = graph_list[top].begin(); it != graph_list[top].end(); ++it){
if(nodeArr[(*it).vertex].dist == INFINITY){
nodeArr[(*it).vertex].dist = nodeArr[top].dist + 1;
nodeArr[(*it).vertex].path = top;
que.push((*it).vertex);
}
}
}
}
測試主函式:
int main(int argc, char *argv[])
{
char *topo[5000];
int edge_num;
char *demand;
int demand_num;
char *topo_file = argv[1];
edge_num = read_file(topo, 5000, topo_file);
if (edge_num == 0)
{
printf("Please input valid topo file.\n");
return -1;
}
int src;
cout << "輸入求最短路徑的起點:";
cin >> src;
Graph G(topo, edge_num);
G.print();
cout << "演算法1: " << endl;
G.unwightedShorestPath(src);
G.printShorestPath();
cout << "演算法2:" << endl;
G.unwightedShorestPathAdv(src);
G.printShorestPath();
release_buff(topo, edge_num);
return 0;
}
測試的圖資料:
1,1,2,1
2,1,4,1
3,2,4,1
4,2,5,1
5,3,1,1
6,3,6,1
7,4,3,1
8,4,6,1
9,4,5,1
10,4,7,1
11,5,7,1
12,7,6,1
第1列表示邊的編號,第2列表示邊的起點,第3列表示邊的終點,第4列表示邊的權重。因為此時是無權圖,所以邊的權重為1。
下面是圖鄰接表類的原始碼:
#ifndef GRAPH_H
#define GRAPH_H
#include <list>
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <iterator>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <queue>
using namespace std;
#define MAX_VERTEX_NUM 600
#define INFINITY 1000000//將INFINITY定義為無窮大的值
//儲存每個頂點資訊的資料結構
struct GraphNode{
bool known;//當前頂點距離起點的距離是否確定
int dist;//當前頂點到起點的最短距離
int path;//當前頂點距離起點的最短路徑的前一個頂點
};
//圖節點資訊
typedef struct Node{
int edge_num;//邊號
int src;//源點
int vertex;//自身
int weight;//邊的權重
}Node;
/*******************************************************
* 類名稱: 鄰接表圖
********************************************************/
class Graph{
private:
int edge_num;//圖邊的個數
int vertex_num;//圖的頂點數目
list<Node> * graph_list;//鄰接表
vector<GraphNode> nodeArr;//儲存每個頂點資訊的陣列
public:
Graph(){}
Graph(char* graph[], int edgenum);
~Graph();
void print();
void unwightedShorestPath(int src);//演算法1求最短距離
void unwightedShorestPathAdv(int src);//演算法2求最短距離
void printShorestPath(); //輸出頂點src到各個頂點最短距離的資訊
private:
vector<int> get_graph_value(char* graph[], int columns);
void addEdge(char* graph[], int columns);
};
/*************************************************
* 函式名稱:unwightedShorestPathAdv(int src)
* 功能描述:求無權圖的任意點到其它頂點的距離,
* 該演算法比unwightedShorestPathAdv要好
* 引數列表:src是起點
* 返回結果:void
*************************************************/
void Graph::unwightedShorestPathAdv(int src)
{
queue<int> que;
//步驟1,將各個頂點的dist設定為無窮大
for(int i = 0; i < vertex_num; ++i)
nodeArr[i].dist = INFINITY;
//步驟2,將頂點src的dist欄位設為0
nodeArr[src].dist = 0;
//步驟3,將頂點src壓入佇列que中
que.push(src);
//步驟5
while(!que.empty()){
//步驟4
int top = que.front();//獲得佇列的首元素
que.pop();//彈出佇列的首元素
//遍歷與頂點相鄰的所有頂點
for(list<Node>::iterator it = graph_list[top].begin(); it != graph_list[top].end(); ++it){
if(nodeArr[(*it).vertex].dist == INFINITY){
nodeArr[(*it).vertex].dist = nodeArr[top].dist + 1;
nodeArr[(*it).vertex].path = top;
que.push((*it).vertex);
}
}
}
}
/*************************************************
* 函式名稱:unwightedShorestPath(int src)
* 功能描述:求無權圖的任意點到其它頂點的距離
* 引數列表:src是起點
* 返回結果:void
*************************************************/
void Graph::unwightedShorestPath(int src)
{
//步驟1,初始化配置表
for(int i = 0; i < vertex_num; ++i){
nodeArr[i].known = false;
nodeArr[i].dist = INFINITY;
nodeArr[i].path = 0;
}
//步驟2,先把距離為0的頂點的dist設定為0
nodeArr[src].dist = 0;
//步驟4
for(int currentDist = 0; currentDist < vertex_num; ++currentDist){
//步驟3
for(int i = 0; i < vertex_num; ++i){
if((!nodeArr[i].known) && (nodeArr[i].dist == currentDist)){
nodeArr[i].known = true;
//遍歷與頂點i相鄰的所有頂點
for(list<Node>::iterator it = graph_list[i].begin(); it != graph_list[i].end(); ++it){
if(nodeArr[(*it).vertex].dist == INFINITY){
nodeArr[(*it).vertex].dist = currentDist + 1;
nodeArr[(*it).vertex].path = i;
}
}
}
}
}
}
/*************************************************
* 函式名稱:printShorestPath()
* 功能描述:將獲得的src頂點到其它頂點的最短路徑輸出
* 引數列表:無
* 返回結果:無
*************************************************/
void Graph::printShorestPath()
{
cout << "頂點\t" << "known\t" << "dist\t" << "path" << endl;
for(int i = 0; i < vertex_num; ++i){
if(nodeArr[i].known)
cout << i << "\t" << nodeArr[i].known << "\t" << nodeArr[i].dist << "\t" << nodeArr[i].path << endl;
}
}
/*************************************************
* 函式名稱:print
* 功能描述:將圖的資訊以鄰接表的形式輸出到標準輸出
* 引數列表:無
* 返回結果:無
*************************************************/
void Graph::print()
{
cout << "******************************************************************" << endl;
//for(int i = 0 ; i < MAX_VERTEX_NUM; ++i){
for(int i = 0 ; i < vertex_num; ++i){
if(graph_list[i].begin() != graph_list[i].end()){
cout << i << "-->";
for(list<Node>::iterator it = graph_list[i].begin(); it != graph_list[i].end(); ++it){
cout << (*it).vertex << "(邊號:" << (*it).edge_num << ",權重:" << (*it).weight << ")-->";
}
cout << "NULL" << endl;
}
}
cout << "******************************************************************" << endl;
}
/*************************************************
* 函式名稱:get_graph_value
* 功能描述:將圖的每一條邊的資訊儲存到一個陣列中
* 引數列表: graph:指向圖資訊的二維陣列
columns:圖的第幾條邊
* 返回結果:無
*************************************************/
vector<int> Graph::get_graph_value(char* graph[], int columns)
{
vector<int> v;
char buff[20];
int i = 0, j = 0, val;
memset(buff, 0, 20);
while((graph[columns][i] != '\n') && (graph[columns][i] != '\0')){
if(graph[columns][i] != ','){
buff[j] = graph[columns][i];
j++;
}
else{
j = 0;
val = atoi(buff);
v.push_back(val);
memset(buff, 0, 20);
}
i++;
}
val = atoi(buff);
v.push_back(val);
return v;
}
/*************************************************
* 函式名稱:addEdge
* 功能描述:將圖的每一條邊的資訊加入圖的鄰接表中
* 引數列表:graph:指向圖資訊的二維陣列
columns:圖的第幾條邊
* 返回結果:無
*************************************************/
void Graph::addEdge(char* graph[], int columns)
{
Node node;
vector<int> v = get_graph_value(graph, columns);
node.edge_num = v[0];
node.src = v[1];
node.vertex = v[2];
node.weight = v[3];
//根據頂點的標號,求的總的頂點數目
if(node.vertex > vertex_num)
vertex_num = node.vertex;
//要考慮重複的邊,但是邊的權重不一樣
for(list<Node>::iterator it = graph_list[node.src].begin(); it != graph_list[node.src].end(); ++it){
if((*it).vertex == node.vertex){
if((*it).weight > node.weight){
(*it).weight = node.weight;
}
return;
}
}
graph_list[node.src].push_back(node);
}
/*************************************************
* 函式名稱:建構函式
* 功能描述:以鄰接表的形式儲存圖的資訊,並儲存必須經過的頂點
* 引數列表:graph:指向圖資訊的二維陣列
edgenum:圖的邊的個數
* 返回結果:無
*************************************************/
Graph::Graph(char* graph[], int edgenum):nodeArr(MAX_VERTEX_NUM)
{
edge_num = edgenum;
vertex_num = 0;
graph_list = new list<Node>[MAX_VERTEX_NUM+1];
for(int i = 0; i < edgenum; ++i){
addEdge(graph, i);
}
vertex_num++;
}
/*************************************************
* 函式名稱:解構函式
* 功能描述:釋放動態分配的記憶體
* 引數列表:無
* 返回結果:無
*************************************************/
Graph::~Graph()
{
delete[] graph_list;
}
#endif
下面是測試函式的程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <sys/timeb.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include "graphShorestPath.h"
#define MAX_LINE_LEN 4000
int read_file(char ** const buff, const unsigned int spec, const char * const filename);
void release_buff(char ** const buff, const int valid_item_num);
int main(int argc, char *argv[])
{
char *topo[5000];
int edge_num;
char *demand;
int demand_num;
char *topo_file = argv[1];
edge_num = read_file(topo, 5000, topo_file);
if (edge_num == 0)
{
printf("Please input valid topo file.\n");
return -1;
}
int src;
cout << "輸入求最短路徑的起點:";
cin >> src;
Graph G(topo, edge_num);
G.print();
cout << "演算法1: " << endl;
G.unwightedShorestPath(src);
G.printShorestPath();
cout << "演算法2:" << endl;
G.unwightedShorestPathAdv(src);
G.printShorestPath();
release_buff(topo, edge_num);
return 0;
}
/****************************************************************
* 函式名稱:read_file
* 功能描述: 讀取檔案中的圖的資料資訊
* 引數列表: buff是將檔案讀取的圖資訊儲存到buff指向的二維陣列中
* spec是檔案中圖最大允許的邊的個數
* filename是要開啟的圖檔案
* 返回結果:無
*****************************************************************/
int read_file(char ** const buff, const unsigned int spec, const char * const filename)
{
FILE *fp = fopen(filename, "r");
if (fp == NULL)
{
printf("Fail to open file %s, %s.\n", filename, strerror(errno));
return 0;
}
printf("Open file %s OK.\n", filename);
char line[MAX_LINE_LEN + 2];
unsigned int cnt = 0;
while ((cnt < spec) && !feof(fp))
{
line[0] = 0;
fgets(line, MAX_LINE_LEN + 2, fp);
if (line[0] == 0) continue;
buff[cnt] = (char *)malloc(MAX_LINE_LEN + 2);
strncpy(buff[cnt], line, MAX_LINE_LEN + 2 - 1);
buff[cnt][4001] = 0;
cnt++;
}
fclose(fp);
printf("There are %d lines in file %s.\n", cnt, filename);
return cnt;
}
/****************************************************************
* 函式名稱:release_buff
* 功能描述: 釋放剛才讀取的檔案中的圖的資料資訊
* 引數列表: buff是指向檔案讀取的圖資訊
* valid_item_num是指圖中邊的個數
* 返回結果:void
*****************************************************************/
void release_buff(char ** const buff, const int valid_item_num)
{
for (int i = 0; i < valid_item_num; i++)
free(buff[i]);
}
下面是執行結果:
相關文章
- 資料結構與演算法——最短路徑Dijkstra演算法的C++實現資料結構演算法C++
- 演算法與資料結構之-圖的最短路徑演算法資料結構
- 資料結構與演算法——B樹的C++實現資料結構演算法C++
- 資料結構與演算法——AVL樹類的C++實現資料結構演算法C++
- 資料結構與演算法——普通樹的定義與C++實現資料結構演算法C++
- 資料結構和演算法學習筆記九:最短路徑資料結構演算法筆記
- 資料結構 最短路徑之—迪傑斯特拉演算法資料結構演算法
- 資料結構與演算法——有向無環圖的拓撲排序C++實現資料結構演算法排序C++
- 資料結構與演算法——不相交集類的C++實現資料結構演算法C++
- 最短路徑演算法總結演算法
- 資料結構學習(C++)——圖【4】(最短路徑) (轉)資料結構C++
- SQL Server與最短路徑演算法SQLServer演算法
- 演算法與資料結構-棧(Stack)-Java實現演算法資料結構Java
- 資料結構與演算法——常用高階資料結構及其Java實現資料結構演算法Java
- 資料結構與演算法——二叉查詢樹類的C++實現資料結構演算法C++
- 最短路徑演算法演算法
- 資料結構與演算法——插入排序以及C++函式模板實現資料結構演算法排序C++函式
- 資料結構與演算法——圖的鄰接表表示法類的C++實現資料結構演算法C++
- 資料結構與演算法 | 棧的實現及應用資料結構演算法
- 【演算法資料結構Java實現】歐幾里得演算法演算法資料結構Java
- 演算法與資料結構——選擇排序(c++)演算法資料結構排序C++
- 資料結構與演算法 | 如何實現LRU快取淘汰演算法資料結構演算法快取
- 資料結構與演算法——表示式樹類的C++實現(二叉樹)資料結構演算法C++二叉樹
- 資料結構:初識(資料結構、演算法與演算法分析)資料結構演算法
- 資料結構與演算法-資料結構(棧)資料結構演算法
- 資料結構與演算法資料結構演算法
- 『資料結構與演算法』棧:詳解與程式碼實現資料結構演算法
- [資料結構與演算法]佇列Queue 的多種實現資料結構演算法佇列
- 最短路徑(Floyd演算法)演算法
- Djikstra最短路徑演算法演算法
- 資料結構與演算法——雜湊表類的C++實現(探測雜湊表)資料結構演算法C++
- 資料結構與演算法——優先佇列類的C++實現(二叉堆)資料結構演算法佇列C++
- 資料結構與演算法——雜湊表類的C++實現(分離連結雜湊表)資料結構演算法C++
- [資料結構與演算法] 排序演算法資料結構演算法排序
- 資料結構與演算法系列(一)陣列實現資料結構演算法陣列
- 【資料結構】 各種排序演算法的實現資料結構排序演算法
- 【演算法與資料結構專場】BitMap演算法基本操作程式碼實現演算法資料結構
- 幾個最短路徑的演算法演算法