十多年前学的数据结构问题和算法问题,闲来拿出来复习复习,权当练练手。本代码所用的方法并非最高效的实现方法,纯以练手为目的,实现过程中尽量把能用上的STL容器挨个用了一遍。解题过程中一些思考都写在代码中的注释里了,顺便重新整理一下思路。
此代码兼容联通图和非联通图。不完全测试了一些边界条件,如有没考虑周全的地方欢迎拍砖或提供test case。
废话不多说,看东西。
EWDigraph.h头文件定义
#pragma once
#include
#include
#include
#include
#include
#include
#include
EWD 同时是Edge Weighted Digraph和Edsger Wybe Dijkstra 的缩写,不知是不是巧合...
const int vex, const int edge 参数中的const是向编译器承诺在函数中不对该参数进行修改。如果该参数在函数体内被改变,则会收到编译器报错。
const vector
图的创建
vector> g = {
make_tuple('E', 'F', 0.35),
make_tuple('F', 'E', 0.35),
make_tuple('E', 'H', 0.37),
make_tuple('F', 'H', 0.28),
make_tuple('H', 'F', 0.28),
make_tuple('F', 'B', 0.32),
make_tuple('A', 'E', 0.38),
make_tuple('A', 'C', 0.26),
make_tuple('H', 'D', 0.39),
make_tuple('B', 'D', 0.29),
make_tuple('C', 'H', 0.34),
make_tuple('G', 'C', 0.40),
make_tuple('D', 'G', 0.52),
make_tuple('G', 'A', 0.58),
make_tuple('G', 'E', 0.93),
make_tuple('I', 'J', 0.5),
make_tuple('J', 'J', 0)
};
auto ewd = make_unique(10,17,g);
ewd->Print();
边带权的单向图。注意:I和J与其他顶点非联通。
打印结果
A | A->E:0.38 A->C : 0.26
B | B->D : 0.29
C | C->H : 0.34
D | D->G : 0.52
E | E->F : 0.35 E->H : 0.37
F | F->E : 0.35 F->H : 0.28 F->B : 0.32
G | G->C : 0.4 G->A : 0.58 G->E : 0.93
H | H->F : 0.28 H->D : 0.39
I | I->J : 0.5
J | J->J : 0
广度优先遍历实现思路:
- 把出发点压入队列
- 读取队列头,将该顶点标记为“已访问”
- 把队列头指向的所有未访问的顶点压入队尾
- 重复第2,3步,直到队列为空
- 检查图中是否还有顶点未访问 (非连通顶点),如果是,则将该顶点入队列,重复2,3,4,5
void EWDigraph::BFST(const char start){
//也可以使用queue, 区别是不能push_front
std::deque q;
map vVisited;
for (auto i : GetNodes()) {
//所有顶点标记为未访问
vVisited[i] = false;
}
//出发点入队列、
q.push_front(start);
cout << "BFST(" << start << "):"<
调用方法
for (auto it : ewd->GetNodes()){
ewd->BFST(it);
cout << endl;
}
打印结果
BFST(A) :
A - E - C - F - H - B - D - G - I - J - end
BFST(B) :
B - D - G - C - A - E - H - F - I - J - end
BFST(C) :
C - H - F - D - E - B - G - A - I - J - end
BFST(D) :
D - G - C - A - E - H - F - B - I - J - end
BFST(E) :
E - F - H - B - D - G - C - A - I - J - end
BFST(F) :
F - E - H - B - D - G - C - A - I - J - end
BFST(G) :
G - C - A - E - H - F - D - B - I - J - end
BFST(H) :
H - F - D - E - B - G - C - A - I - J - end
BFST(I) :
I - J - A - E - C - F - H - B - D - G - end
BFST(J) :
J - A - E - C - F - H - B - D - G - I - end
深度优先遍历实现思路:
- 把当前图中所有顶点压入栈 (防止有非联通顶点未被遍历)
- 出发点入栈
- 读取栈顶,将该顶点标记为“已访问”
- 把栈顶指向的所有未访问的顶点入栈
- 重复第3,4步,直到队列为空
void EWDigraph::DFST(const char start){
std::stack s;
map vVisited;
for (auto i : GetNodes()) {
vVisited[i] = false;
s.push(i);
}
//出发点处于栈顶
s.push(start);
cout << "DFST(" << start << "):"<
调用方法
for (auto it : ewd->GetNodes()){
ewd->DFST(it);
cout << endl;
}
打印结果D
DFST(A) :
A - C - H - D - G - E - F - B - J - I - end
DFST(B) :
B - D - G - E - H - F - A - C - J - I - end
DFST(C) :
C - H - D - G - E - F - B - A - J - I - end
DFST(D) :
D - G - E - H - F - B - A - C - J - I - end
DFST(E) :
E - H - D - G - A - C - F - B - J - I - end
DFST(F) :
F - B - D - G - E - H - A - C - J - I - end
DFST(G) :
G - E - H - D - F - B - A - C - J - I - end
DFST(H) :
H - D - G - E - F - B - A - C - J - I - end
DFST(I) :
I - J - H - D - G - E - F - B - A - C - end
DFST(J) :
J - I - H - D - G - E - F - B - A - C - end
Dijkstra最短路径实现思路(此方法不支持路径为负)
- 将出发点(标记为S)到自己的距离设为0,到所有其他顶点的距离设为无穷大,所有其他顶点路径标记为“不可达到”
- 出发点压入队列,将出发点标记为“已在队列中”
- 读取队列头(标记为H),将其标记为“未在队列中”
- 遍历所有与队列头相邻的顶点(标记为N1,N2,...)。如果S到N1(N2,...)的距离(初始值是无穷大) > S到H的距离+H到N1(N2,...)的距离时,记录:S到N1的距离=S到H距离+H到N1(N2,...)的距离
- 使用mapPath_记录当前状态下S到N1(N2,...)的最短路径必须经过顶点H,该状态在后续的遍历中可能会改变
- 检查N1,N2,...是否在队列中,如果不在,则将其压入队列
- 重复3,4,5,6,直到队列为空
void EWDigraph::Dijkstra(const char start){
InitVecs();
cout << "Start Point:" << start << endl;
//出发点到自身的距离为0
mapDistance_[start] = 0;
std::queue q;
//出发点入队列
q.push(start);
//标记已在队列中
mapIsInQue_[start] = true;
while (!q.empty()){
//队列头出队列
int node = q.front();
q.pop();
mapIsInQue_[node] = false;
for (auto it : ewd_[node]){
//遍历所有与当前顶点(队列头顶点)相邻的顶点
if (mapDistance_[it.first] > mapDistance_[node] + it.second){
mapDistance_[it.first] = mapDistance_[node] + it.second;
//记录当前到达该相邻顶点的最短路径需要经过当前顶点(队列头顶点)
mapPath_[it.first] = node;
if (!mapIsInQue_[it.first]){
//如果这些与当前顶点相邻的顶点不在队列中,则将其压入队列
q.push(it.first);
mapIsInQue_[it.first] = true;
}
}
}
}
PrintDij(start);
}
Dijkstra最短路径打印实现思路
- 如果目标顶点T被标记为"不可达到"则打印"unreachable",否则将T入栈
- 遍历mapPath_,找到到达目标顶点T必须经过的前一个顶点T-1
- 将T-1入栈,重复2找到到达目标顶点必须经过的前一个顶点T-2
- 以此类推,直到找到出发点S,将S入栈
- 根据出栈顺序依次打印站内顶点
void EWDigraph::PrintDij(const char start){
for (auto im:ewd_){
cout << im.first << ":" << mapDistance_[im.first] << " | Path = " ;
std::stack spath;
for (int j = im.first; j != start; j = mapPath_[j]){
if (j == '0'){
cout << "unreachable " ;
break;
}
else{
spath.push(j);
}
}
spath.push(start);
while (spath.size()>0){
cout << spath.top();
if (spath.top() != im.first)
cout << "->";
spath.pop();
}
cout << endl;
}
cout << endl;
}
运行结果
Start Point : A
A : 0 | Path = A
B : 1.05 | Path = A->E->F->B
C : 0.26 | Path = A->C
D : 0.99 | Path = A->C->H->D
E : 0.38 | Path = A->E
F : 0.73 | Path = A->E->F
G : 1.51 | Path = A->C->H->D->G
H : 0.6 | Path = A->C->H
I : 1.79769e+308 | Path = unreachable A->I
J : 1.79769e+308 | Path = unreachable A->J
Start Point : B
A : 1.39 | Path = B->D->G->A
B : 0 | Path = B
C : 1.21 | Path = B->D->G->C
D : 0.29 | Path = B->D
E : 1.74 | Path = B->D->G->E
F : 1.83 | Path = B->D->G->C->H->F
G : 0.81 | Path = B->D->G
H : 1.55 | Path = B->D->G->C->H
I : 1.79769e+308 | Path = unreachable B->I
J : 1.79769e+308 | Path = unreachable B->J
Start Point : C
A : 1.83 | Path = C->H->D->G->A
B : 0.94 | Path = C->H->F->B
C : 0 | Path = C
D : 0.73 | Path = C->H->D
E : 0.97 | Path = C->H->F->E
F : 0.62 | Path = C->H->F
G : 1.25 | Path = C->H->D->G
H : 0.34 | Path = C->H
I : 1.79769e+308 | Path = unreachable C->I
J : 1.79769e+308 | Path = unreachable C->J
Start Point : D
A : 1.1 | Path = D->G->A
B : 1.86 | Path = D->G->C->H->F->B
C : 0.92 | Path = D->G->C
D : 0 | Path = D
E : 1.45 | Path = D->G->E
F : 1.54 | Path = D->G->C->H->F
G : 0.52 | Path = D->G
H : 1.26 | Path = D->G->C->H
I : 1.79769e+308 | Path = unreachable D->I
J : 1.79769e+308 | Path = unreachable D->J
Start Point : E
A : 1.86 | Path = E->H->D->G->A
B : 0.67 | Path = E->F->B
C : 1.68 | Path = E->H->D->G->C
D : 0.76 | Path = E->H->D
E : 0 | Path = E
F : 0.35 | Path = E->F
G : 1.28 | Path = E->H->D->G
H : 0.37 | Path = E->H
I : 1.79769e+308 | Path = unreachable E->I
J : 1.79769e+308 | Path = unreachable E->J
Start Point : F
A : 1.71 | Path = F->B->D->G->A
B : 0.32 | Path = F->B
C : 1.53 | Path = F->B->D->G->C
D : 0.61 | Path = F->B->D
E : 0.35 | Path = F->E
F : 0 | Path = F
G : 1.13 | Path = F->B->D->G
H : 0.28 | Path = F->H
I : 1.79769e+308 | Path = unreachable F->I
J : 1.79769e+308 | Path = unreachable F->J
Start Point : G
A : 0.58 | Path = G->A
B : 1.34 | Path = G->C->H->F->B
C : 0.4 | Path = G->C
D : 1.13 | Path = G->C->H->D
E : 0.93 | Path = G->E
F : 1.02 | Path = G->C->H->F
G : 0 | Path = G
H : 0.74 | Path = G->C->H
I : 1.79769e+308 | Path = unreachable G->I
J : 1.79769e+308 | Path = unreachable G->J
Start Point : H
A : 1.49 | Path = H->D->G->A
B : 0.6 | Path = H->F->B
C : 1.31 | Path = H->D->G->C
D : 0.39 | Path = H->D
E : 0.63 | Path = H->F->E
F : 0.28 | Path = H->F
G : 0.91 | Path = H->D->G
H : 0 | Path = H
I : 1.79769e+308 | Path = unreachable H->I
J : 1.79769e+308 | Path = unreachable H->J
Start Point : I
A : 1.79769e+308 | Path = unreachable I->A
B : 1.79769e+308 | Path = unreachable I->B
C : 1.79769e+308 | Path = unreachable I->C
D : 1.79769e+308 | Path = unreachable I->D
E : 1.79769e+308 | Path = unreachable I->E
F : 1.79769e+308 | Path = unreachable I->F
G : 1.79769e+308 | Path = unreachable I->G
H : 1.79769e+308 | Path = unreachable I->H
I : 0 | Path = I
J : 0.5 | Path = I->J
Start Point : J
A : 1.79769e+308 | Path = unreachable J->A
B : 1.79769e+308 | Path = unreachable J->B
C : 1.79769e+308 | Path = unreachable J->C
D : 1.79769e+308 | Path = unreachable J->D
E : 1.79769e+308 | Path = unreachable J->E
F : 1.79769e+308 | Path = unreachable J->F
G : 1.79769e+308 | Path = unreachable J->G
H : 1.79769e+308 | Path = unreachable J->H
I : 1.79769e+308 | Path = unreachable J->I
J : 0 | Path = J
EWDigraph.cpp完整代码
#include "EWDigraph.h"
EWDigraph::EWDigraph(const int vex, const int edge, const vector> &v)
:vex_(vex),
edge_(edge)
{
//把vector> 转换成map>
//get<0>(v[i])是元组tuple的特殊用法
for (int i = 0; i < edge_; i++)
ewd_[get<0>(v[i])].push_back(make_pair(get<1>(v[i]), get<2>(v[i])));
}
void EWDigraph::Print(){
for (auto a: ewd_){
cout << a.first<< "|";
for (auto it:ewd_[a.first])
cout << a.first << "->" << it.first << ":" << it.second << " ";
cout << endl;
}
cout << endl;
}
vector EWDigraph::GetNodes() const{
vector vRet;
for (auto it : ewd_)
vRet.push_back(it.first);
return vRet;
}
void EWDigraph::BFST(const char start){
//也可以使用queue, 区别是不能push_front
std::deque q;
map vVisited;
for (auto i : GetNodes()) {
//所有顶点标记为未访问
vVisited[i] = false;
}
//出发点入队列、
q.push_front(start);
cout << "BFST(" << start << "):"< s;
map vVisited;
for (auto i : GetNodes()) {
vVisited[i] = false;
s.push(i);
}
//出发点处于栈顶
s.push(start);
cout << "DFST(" << start << "):"< mapPath_
mapPath_[i] = '0';
//map mapIsInQue_
mapIsInQue_[i] = false;
//DBL_MAX = 2^1024
mapDistance_[i] = DBL_MAX;
}
}
void EWDigraph::Dijkstra(const char start){
InitVecs();
cout << "Start Point:" << start << endl;
//出发点到自身的距离为0
mapDistance_[start] = 0;
std::queue q;
//出发点入队列
q.push(start);
//标记已在队列中
mapIsInQue_[start] = true;
while (!q.empty()){
//队列头出队列
int node = q.front();
q.pop();
mapIsInQue_[node] = false;
for (auto it : ewd_[node]){
//遍历所有与当前顶点(队列头顶点)相邻的顶点
if (mapDistance_[it.first] > mapDistance_[node] + it.second){
mapDistance_[it.first] = mapDistance_[node] + it.second;
//记录当前到达该相邻顶点的最短路径需要经过当前顶点(队列头顶点)
mapPath_[it.first] = node;
if (!mapIsInQue_[it.first]){
//如果这些与当前顶点相邻的顶点不在队列中,则将其压入队列
q.push(it.first);
mapIsInQue_[it.first] = true;
}
}
}
}
PrintDij(start);
}
void EWDigraph::PrintDij(const char start){
for (auto im:ewd_){
cout << im.first << ":" << mapDistance_[im.first] << " | Path = " ;
std::stack spath;
for (int j = im.first; j != start; j = mapPath_[j]){
if (j == '0'){
cout << "unreachable " ;
break;
}
else{
spath.push(j);
}
}
spath.push(start);
while (spath.size()>0){
cout << spath.top();
if (spath.top() != im.first)
cout << "->";
spath.pop();
}
cout << endl;
}
cout << endl;
}
void EWDigraph::Dijkstra(const char start, const char end){
//指定起点和终点的最短路径查找,思路同前
InitVecs();
cout << start << "->" << end << ":";
mapDistance_[start] = 0;
std::queue q;
q.push(start);
mapIsInQue_[start] = true;
while (!q.empty()){
int node = q.front();
q.pop();
mapIsInQue_[node] = false;
for (auto it : ewd_[node]){
if (mapDistance_[it.first] > mapDistance_[node] + it.second){
mapDistance_[it.first] = mapDistance_[node] + it.second;
mapPath_[it.first] = node;
if (!mapIsInQue_[it.first]){
q.push(it.first);
mapIsInQue_[it.first] = true;
}
}
}
}
PrintDij(start, end);
}
void EWDigraph::PrintDij(const char start, const char end){
cout << mapDistance_[end] << " | Path = ";
std::stack spath;
for (int j = end; j != start; j = mapPath_[j]){
if (j == '0'){
cout << "unreachable";
break;
}
else{
spath.push(j);
}
}
spath.push(start);
while (spath.size()>0){
cout << spath.top();
if (spath.top() != end)
cout << "->";
spath.pop();
}
cout << endl;
cout << endl;
}
bool EWDigraph::IsValidVex(const char vex) const{
//检查输入的顶点是否合法
return (ewd_.find(vex) != ewd_.end());
}