本程序是本人大二课程设计自己写滴,希望对您有帮助!!
课题设计简要任务:
- 构建公园无向图,领接矩阵或邻接表储存;
- 要求求任意两个景点的最短路径;
- 列出入口到出口的路径(路径条数有上限)。
- 要求用文本读入的方式读取数据
目录
一、景点储存与读入
本人用的是邻接表的方式储存数据
struct Head_Node //头节点,纵向
{
string name;
struct Node* First; //指向的第一个Node
Head_Node()
{
name = "";
First = NULL;
}
};
struct Node //横向节点
{
int index;//对应的下标(Head_Node中)
struct Node* next;
double weight;//米
Node()
{
index = 0;
next = NULL;
weight = int(1<<30);
}
};
接下来是Park_guide类的建立
class Park_guide //有权无向图
{
private:
vector<Head_Node> Net;//总网,纵向数组
static int Max_Path;
static int Path_Num;//路径数
int Spot_Num;//景点数
int Side_Num;//边数
int Min_Path[Max_spot][Max_spot];//最短路径的导引数组,用于查任意两点最短路径
double Map[Max_spot][Max_spot]; //对应的邻接矩阵
//double Min_line[100][100];
public:
Park_guide();//初始化地图
~Park_guide();
void Fp_in();//读入文件的函数
int Find(vector<Head_Node> &x, string y);//返回景点y 在Net中的位置(纵向的坐标)
void Inout_ways();//入口和出口的路径
void Inout_Dfs(int temp, int In, int Out, int *arr, int *path);
void Print_line(int In, int Out, int* path);//打印入口出口路线,最大 Max_Path 条
double Weight(int x, int y);//返回以x - y为边的权值
void Per_Min_line();
void Tran_array();
void Two_Mi_line(string de1, string de2);
int Re_Num() { return Max_Path; }//返回最大路径数
};
上图可以方便理解
接下来是文件的读入
首先文件的读入有格式,如下:
n=7
入口 景点1 景点2 景点3 景点4 景点5 出口
v=10
入口 景点1 5
入口 景点3 10
景点1 景点3 8
景点1 景点2 7
景点2 景点5 7
景点5 景点3 6
景点5 景点4 4
景点4 出口 4
景点3 出口 20
景点2 景点4 5
文件名为Test.txt;
n为景点数(包含入口和出口);
v为边数;
第二行要入口为第一个,出口为最后一个;
之间以空格间隔。
下面是函数实现
void Park_guide::Fp_in()
{
ifstream ifs("Test.txt");//定义文件输入流
(ifs).seekg(0);//定位到文件开头
char ch=' ';
string line="";//用于跳行
while (1)
{
ifs.get(ch);
if (ch == 'n') break;
getline(ifs, line);
}
ifs.get(ch);//把等号忽略掉,从等号之后开始读
int n=0;
ifs >> n; //ifs可以与cin一样输入
Spot_Num = n;
//存景点
for (int i = 0; i < n; ++i)
{
Head_Node* s = new Head_Node();
ifs >> s->name;
Net.push_back(*s);
}
ifs.get(ch);//把回车吃掉
ch = ' ';
line = ' ';
while (1)
{
ifs.get(ch);
if (ch == 'v') break;
getline(ifs, line);
}
int v = 0;
ifs.get(ch);
ifs >> v;
Side_Num = v;
//初始化地图
double w;
string s1, s2;
for (int i = 0; i < Side_Num; ++i)
{
Node* x = new Node();
Node* y = new Node();
//边s1,s2对应(x -w- y)
ifs >> s1 >> s2 >> w;
//x为始点,y为末点, 头插法
y->index = Find(Net, s2);
y->weight = w;
y->next = Net[Find(Net, s1)].First;
Net[Find(Net, s1)].First = y;
//y为始点,x为末点
x->index = Find(Net, s1);
x->weight = w;
x->next = Net[Find(Net, s2)].First;
Net[Find(Net, s2)].First = x;
}
}
二、DFS
用广搜邻接表来找入口与出口之间的路径,需要两个数组(这里用动态开辟了数组)来实现,arr数组用来标记当前是否走过,path数组用来记录路径便于输出
void Park_guide::Inout_ways()
{
int s1, s2;
s1 = Find(Net, Net[0].name);
s2 = Find(Net, Net[Net.size()-1].name);
int *arr = new int[Spot_Num];
int *path = new int[Spot_Num];
memset(arr, 0, Spot_Num*sizeof(int));//标记数组
memset(path, -1, Spot_Num*sizeof(int));//路径
Inout_Dfs(s1, s1, s2, arr, path);
cout << endl;
Path_Num = 0;
delete []arr;
delete []path;
}
void Park_guide::Inout_Dfs(int temp, int In, int Out, int *arr, int *path )
{
if (Path_Num == Max_Path) return;//允许最大路径数
if (temp == Out)//到了出口
{
Print_line(In, Out, path);
Path_Num++;
return;
}
arr[temp] = 1;
Node* s = new Node();
s = Net[temp].First;
while (s != NULL)
{
//cout << s->index << endl;
if (arr[s->index]==0)
{
path[temp] = s->index;
Inout_Dfs(s->index, In, Out, arr, path);
//回溯
arr[s->index] = 0;
path[temp] = -1;
}
s = s->next;
}
}
void Park_guide::Print_line(int In, int Out, int* path)
{
int i = In;
int f = 1;
double all = 0;
if (path[i] == -1)
{
cout << "无路径"<<endl;
return;
}
cout << "路线" <<Path_Num+1<< ": ";
while (path[i] != -1)
{
if (f)
{
cout << Net[i].name;
all += Weight(i, path[i]);
f = 0;
}
else
{
cout << " -> " << Net[i].name;
all += Weight(i, path[i]);
}
i = path[i];
}
//出口节点
cout << " -> " << Net[i].name;
cout << endl << "总长度为:" << all << endl;
}
三、Floyd
用Floyd算法求任意两点的最短路径,需要两个辅助数组实现,Map[][]二维数组用来表示当前地图的邻接矩阵,后续用来表示两个节点之间的最短距离;Min_Path[][]二维数组用来查找最短路径(提前实现,后续用可以直接用)
Min_Path[i][j] 中的元素表示节点i到节点j的最短路径的第一步去的那个节点(初始化为j,即看成i与j有边),如此下去直到索引到节点j,最短路径就出来了
下面是实现
void Park_guide::Per_Min_line()
{
Tran_array();//邻接表转邻接矩阵
//Floyd算法。 以i为中间点,j到k的路径
for (int i = 0; i < Spot_Num; ++i)
{
for (int j = 0; j < Spot_Num; ++j)
{
for (int k = 0; k < Spot_Num; ++k)
{
//发现j到i到k的路比j到k的路还要小,更新路径
if (Map[j][i] + Map[i][k] < Map[j][k])
{
Map[j][k] = Map[j][i] + Map[i][k];
//更新标记点,即j到k的路变为j到i,i到k的不用管
Min_Path[j][k] = Min_Path[j][i];
}
}
}
}
}
void Park_guide::Tran_array()
{
for (int i = 0; i < Spot_Num; ++i)
{
for (int j = 0; j < Spot_Num; ++j)
{
Min_Path[i][j] = j;
if (i == j) Map[i][j] = 0;
else Map[i][j] = int(1<<30);
}
if (Net[i].First)
{
Node* s = new Node();
s = Net[i].First;
while (s)
{
Map[i][s->index] = s->weight;
s = s->next;
}
}
}
}
需要三重循环来实现,有点想动态规划
重点是条件:Map[j][i] + Map[i][k] < Map[j][k] 成立时,即节点j经过节点i再到节点k,比节点j到节点k还要短,就要更新节点,更新两个辅助数组
四、测试
本人自拟的测试图
测试
五、代码
本程序有两部分
1.txt文本文件
把它放进编译文件同目录就可以,注意:文件名格式为"Test.txt"!!!!!!
本人用的时devc++,如图在目录创建一个记事本"Test.txt"文件,然后保存下面的景点数据(也可以自己设计一个地图)
n=7
入口 景点1 景点2 景点3 景点4 景点5 出口
v=10
入口 景点1 5
入口 景点3 10
景点1 景点3 8
景点1 景点2 7
景点2 景点5 7
景点5 景点3 6
景点5 景点4 4
景点4 出口 4
景点3 出口 20
景点2 景点4 5
2.完整代码,嘿嘿
#include<iostream>
#include<vector>
#include<string>
#include<fstream>
#include<string.h>
using namespace std;
#define Max_spot 100 //允许公园最大景点数
struct Head_Node //头节点,纵向
{
string name;
struct Node* First; //指向的第一个Node
Head_Node()
{
name = "";
First = NULL;
}
};
struct Node //横向节点
{
int index;//对应的下标(Head_Node中)
struct Node* next;
double weight;//米
Node()
{
index = 0;
next = NULL;
weight = int(1<<30);
}
};
class Park_guide //有权无向图
{
private:
vector<Head_Node> Net;//总网,纵向数组
static int Max_Path;
static int Path_Num;//路径数
int Spot_Num;//景点数
int Side_Num;//边数
int Min_Path[Max_spot][Max_spot];//最短路径的导引数组,用于查任意两点最短路径
double Map[Max_spot][Max_spot]; //对应的邻接矩阵
//double Min_line[100][100];
public:
Park_guide();//初始化地图
~Park_guide();
void Fp_in();//读入文件的函数
int Find(vector<Head_Node> &x, string y);//返回景点y 在Net中的位置(纵向的坐标)
void Inout_ways();//入口和出口的路径
void Inout_Dfs(int temp, int In, int Out, int *arr, int *path);
void Print_line(int In, int Out, int* path);//打印入口出口路线,最大 Max_Path 条
double Weight(int x, int y);//返回以x - y为边的权值
void Per_Min_line();
void Tran_array();
void Two_Mi_line(string de1, string de2);
int Re_Num() { return Max_Path; }//返回最大路径数
};
//实现
int Park_guide:: Path_Num = 0;
int Park_guide::Max_Path = 5;
Park_guide::Park_guide()
{
Fp_in();
Per_Min_line();//先构建最短路径图,后面要用直接用
}
Park_guide::~Park_guide()
{
}
int Park_guide::Find(vector<Head_Node> &x, string y)
{
for (unsigned int i = 0; i < x.size(); ++i)
{
if (x[i].name == y) return i;
}
return -1;
}
void Park_guide::Inout_ways()
{
int s1, s2;
s1 = Find(Net, Net[0].name);
s2 = Find(Net, Net[Net.size()-1].name);
int *arr = new int[Spot_Num];
int *path = new int[Spot_Num];
memset(arr, 0, Spot_Num*sizeof(int));//标记数组
memset(path, -1, Spot_Num*sizeof(int));//路径
Inout_Dfs(s1, s1, s2, arr, path);
cout << endl;
Path_Num = 0;
delete []arr;
delete []path;
}
void Park_guide::Inout_Dfs(int temp, int In, int Out, int *arr, int *path )
{
if (Path_Num == Max_Path) return;//允许最大路径数
if (temp == Out)//到了出口
{
Print_line(In, Out, path);
Path_Num++;
return;
}
arr[temp] = 1;
Node* s = new Node();
s = Net[temp].First;
while (s != NULL)
{
//cout << s->index << endl;
if (arr[s->index]==0)
{
path[temp] = s->index;
Inout_Dfs(s->index, In, Out, arr, path);
//回溯
arr[s->index] = 0;
path[temp] = -1;
}
s = s->next;
}
}
void Park_guide::Print_line(int In, int Out, int* path)
{
int i = In;
int f = 1;
double all = 0;
if (path[i] == -1)
{
cout << "无路径"<<endl;
return;
}
cout << "路线" <<Path_Num+1<< ": ";
while (path[i] != -1)
{
if (f)
{
cout << Net[i].name;
all += Weight(i, path[i]);
f = 0;
}
else
{
cout << " -> " << Net[i].name;
all += Weight(i, path[i]);
}
i = path[i];
}
//出口节点
cout << " -> " << Net[i].name;
cout << endl << "总长度为:" << all << endl;
}
double Park_guide::Weight(int x, int y)
{
Node* s = new Node();
s = Net[x].First;
double wei=0;
while (s != NULL)
{
if (s->index == y)
{
wei = s->weight;
}
s = s->next;
}
return wei;
}
void Park_guide::Per_Min_line()
{
Tran_array();//邻接表转邻接矩阵
//Floyd算法。 以i为中间点,j到k的路径
for (int i = 0; i < Spot_Num; ++i)
{
for (int j = 0; j < Spot_Num; ++j)
{
for (int k = 0; k < Spot_Num; ++k)
{
//发现j到i到k的路比j到k的路还要小,更新路径
if (Map[j][i] + Map[i][k] < Map[j][k])
{
Map[j][k] = Map[j][i] + Map[i][k];
//更新标记点,即j到k的路变为j到i,i到k的不用管
Min_Path[j][k] = Min_Path[j][i];
}
}
}
}
}
void Park_guide::Tran_array()
{
for (int i = 0; i < Spot_Num; ++i)
{
for (int j = 0; j < Spot_Num; ++j)
{
Min_Path[i][j] = j;
if (i == j) Map[i][j] = 0;
else Map[i][j] = int(1<<30);
}
if (Net[i].First)
{
Node* s = new Node();
s = Net[i].First;
while (s)
{
Map[i][s->index] = s->weight;
s = s->next;
}
}
}
}
void Park_guide::Two_Mi_line(string de1, string de2)
{
int x = Find(Net, de1);
int y = Find(Net, de2);
int t = x;
//输入有问题的情况
if (x == -1 || y == -1)
{
cout << "请输入正确的景点名称!" << endl<<"重新输入序号!"<<endl<<endl;
return;
}
//自己到达自己的情况
if (x == y)
{
cout << "已经到达景点" << endl;
return;
}
cout << "最短游览路线:" << endl;
cout << Net[x].name;
while (Min_Path[x][y] != y)
{
cout <<" -> "<< Net[Min_Path[x][y]].name;
x = Min_Path[x][y];
}
cout << " -> " << Net[Min_Path[x][y]].name << endl;
cout << "总长度为:" << Map[t][y] <<"米"<< endl << endl;
}
void Park_guide::Fp_in()
{
ifstream ifs("Test.txt");//定义文件输入流
(ifs).seekg(0);//定位到文件开头
char ch=' ';
string line="";//用于跳行
while (1)
{
ifs.get(ch);
if (ch == 'n') break;
getline(ifs, line);
}
ifs.get(ch);//把等号忽略掉,从等号之后开始读
int n=0;
ifs >> n; //ifs可以与cin一样输入
Spot_Num = n;
//存景点
for (int i = 0; i < n; ++i)
{
Head_Node* s = new Head_Node();
ifs >> s->name;
Net.push_back(*s);
}
ifs.get(ch);//把回车吃掉
ch = ' ';
line = ' ';
while (1)
{
ifs.get(ch);
if (ch == 'v') break;
getline(ifs, line);
}
int v = 0;
ifs.get(ch);
ifs >> v;
Side_Num = v;
//初始化地图
double w;
string s1, s2;
for (int i = 0; i < Side_Num; ++i)
{
Node* x = new Node();
Node* y = new Node();
//边s1,s2对应(x -w- y)
ifs >> s1 >> s2 >> w;
//x为始点,y为末点, 头插法
y->index = Find(Net, s2);
y->weight = w;
y->next = Net[Find(Net, s1)].First;
Net[Find(Net, s1)].First = y;
//y为始点,x为末点
x->index = Find(Net, s1);
x->weight = w;
x->next = Net[Find(Net, s2)].First;
Net[Find(Net, s2)].First = x;
}
}
//主函数
int main()
{
ios::sync_with_stdio(0); cin.tie(0);
static Park_guide test;
int temp = -1;
cout << "欢迎来到公园!!!!" << endl<<endl;
while (1)
{
temp = -1;
cout << "*****************************" << endl;
cout << "* 请输入序号: *" << endl;
cout << "* 1:游览两个景点的最短路径 *" << endl;
cout << "* 2:游览路线(入口到出口) *" << endl;
cout << "* 0:quit *" << endl;
cout << "*****************************" << endl;
scanf("%d", &temp);
if ( temp == 1 || temp == 2 || temp == 0)
switch (temp)
{
case 1:
{
cout << "请输入您要游览的两个景点(空格间隔):" << endl;
string s1, s2;
cin >> s1 >> s2;
test.Two_Mi_line(s1, s2);
break;
}
case 2:
{
cout << "为您输入最多"<<test.Re_Num()<<"条的路径" << endl;
test.Inout_ways();
break;
}
case 0:
{
cout << "正常退出" << endl;
return 0;
}
}
else
{
cout << "非正常退出" << endl;
return 0;
}
}
return 0;
}
到这也就结束啦,这篇文章也是我第一次发表的,看起来有的多,有的乱,哪里有错误或不解的地方可以评论告诉我,我的确尽了很大努力了,就当作是锻炼自己。希望我自己的拙见能对大家有帮助吧,祝大家身体健康,快乐每一天!