前言
做课设的时候思路总是不清晰,就想着边写博客边做题好了,随便把思路可以写下来。
题目要求
给出两个文件,小规模数据文件graph_small和大规模数据文件graph_big.
图数据文件格式如下:
起始点ID 下游点1(ID、距离) 下游点2 下游点3 下游点4
任务1:输出graph_big里度最高的前10个顶点,输出到屏幕。
任务2:给定两个顶点(graph_big),进行连通性判断,即连通或不连通。
任务3:任给一个graph_big里的顶点,采用深度优先遍历和广度优先遍历,输出遍历结果(包括所有的顶点和部分边),将结果写入文件graph1.txt和graph2.txt。文件格式参考graph_small和graph_big。
任务4:输出graph_big的最小生成树(Kruskal算法或者Prim算法)
任务5:给定两个顶点ID(graph_big),求解最短路径,要求输出最短路径长度和最短路径上的每个顶点。
思路:把图创建好基本就没问题了。。。反正小任务都可以找得到代码(懒狗)
难点:主要是注意是数据的格式(从文件中读取,为什么就不能自己输入呢?害我不能直接ctrl+c。。。难受),其次就是申请空间的大小(graph_big有130多万条数据,我觉得运行时间肯定很长,就用graph_small文件去完成了,能做出部分就是胜利。。。欸嘿)
图的邻接表存储结构的类型声明
struct Arc{
int adjvex;
int weight;
Arc *nextarc;
};
struct Vertex{
int code;
Arc *firstarc;
};
struct Graph{
Vertex *v;
int vexnum,arcnum;
// int *vexcode;
//GraphKind type;
};
基于邻接表表示法的有向图的创建
代码如下:
void CreateGraph(Graph &G){
fstream graph_big;
char s[200],ch,s1[200];
int l = 0,vexnum = 0,arcnum = 0;
graph_big.open("graph_big.txt",ios::in);
if(!graph_big){
cout << "打开文件失败!" << endl;
exit(0);
}
graph_big.getline(s,200);
while(!graph_big.eof()){
graph_big.get(ch);
if(ch != '\n'){
s1[l] = ch;
l++;
}
else{
s1[l] = ch;
int flag = 0;
for(int k = 0;k <= l;k ++){
if(flag == 0){
while(s1[k] != '\t'){
if(s1[k] >= '0' && s1[k] <= '9'){
int num = s1[k] - '0';
G.v[vexnum].code = G.v[vexnum].code * 10 + num;
}
k++;
}
vexnum++;
flag = 1;
}
else if(flag == 1){
if(s1[k] == '\t')
continue;
else if(s1[k] == '\n')
break;
else if(s1[k] >= '0' && s1[k] <= '9'){
Arc *p = (Arc *)malloc(sizeof(Arc));
p->adjvex = p->weight = 0;
p->nextarc = NULL;
while(s1[k] != '('){
if(s1[k] >= '0' && s1[k] <= '9'){
int num = s1[k] - '0';
p->adjvex = p->adjvex * 10 + num;
}
k++;
}
while(s1[k] != ')'){
if(s1[k] >= '0' && s1[k] <= '9'){
int num = s1[k] - '0';
p->weight = p->weight * 10 + num;
}
k++;
}
arcnum++;
if(G.v[vexnum - 1].firstarc == NULL){
G.v[vexnum - 1].firstarc = p;
G.v[vexnum - 1].firstarc->nextarc = NULL;
}
else{
Arc *p1 = G.v[vexnum - 1].firstarc;
if(p1->nextarc == NULL){
p1->nextarc = p;
p1->nextarc->nextarc = NULL;
}
else{
while(p1->nextarc != NULL)
p1 = p1->nextarc;
p1->nextarc = p;
p1->nextarc->nextarc = NULL;
}
}
}
}
}
l = 0;
}
}
G.vexnum = vexnum;
G.arcnum = arcnum;
graph_big.close();
}
遇到的问题:1.读文件的时候试过用getline,但是读出来的字符串总是是空的,后面用strlen函数测试长度也总是为0,我觉得应该是因为文件中的第一个数据是‘0’而发生的字符截断,导致字符串长度为0(具体还是写完后再测试一下)
2.用graph_big会有运行超时的问题
解决方法:1.用get函数一个字符一个字符地读取,同时用一个变量l记录长度(用strlen的长度为0)
2.我觉得这里还是得用getline,后面再改改。。。,先用gragh_small凑合用用
至于怎么生成有向图,我的提议是不如看书~
任务一
输出度最大的十个顶点,因为是有向图,所以有入度和出度,统计好每个顶点的度数后,有很多方法得到十个顶点,这里是每次选取最大的度数放在第一位,输出后置为-1,循环十次,得出结果。
代码如下:
void CompareDegree(Graph G){//输出度最大的十个顶点
int *degree = new int[500]{0};
for(int i = 0;i < G.vexnum;i ++){
int code = G.v[i].code;
Arc *a = G.v[i].firstarc;
while(a != NULL){
degree[code]++;
degree[a->adjvex]++;
a = a->nextarc;
}
}
for(int i = 0;i < 10;i ++){
int max = degree[0];
for(int j = 0;j < G.vexnum;j ++){
if(max < degree[j])
max = degree[j];
}
for(int k = 0;k < G.vexnum;k ++){
if(max == degree[k]){
cout << k << endl;
degree[k] = -1;
break;
}
}
}
delete[] degree;
}
任务二
判断两个顶点的连通性:给出两个顶点,分别用深度优先遍历标记它们所经过的顶点,若两次遍历中两个顶点都被标记,则两个顶点是强连通的,若只有一个顶点,则为单向连通,若都未被标记,则不连通。
附代码:
void DFS(Graph G,int p){//深度优先遍历
visited1[p] = 1;
Arc *t = G.v[p].firstarc;
while(t != NULL){
if(visited1[t->adjvex] == 0)
DFS(G,t->adjvex);
t = t->nextarc;
}
}
void DFS2(Graph G,int p){//深度优先遍历
visited2[p] = 1;
Arc *t = G.v[p].firstarc;
while(t != NULL){
if(visited2[t->adjvex] == 0)
DFS2(G,t->adjvex);
t = t->nextarc;
}
}
bool isConnected(int p1,int p2){//判断是否连通
if(visited1[p2] == 1 && visited2[p1] == 1)
cout << "顶点" << p1 << "和顶点" << p2 << "是强连通的" << endl;
else if(visited1[p2] == 1 || visited2[p1] == 1)
cout << "顶点" << p1 << "和顶点" << p2 << "是单向连通的" << endl;
else
cout << "顶点" << p1 << "和顶点" << p2 << "是不连通的" << endl;
}
任务三
给定一个顶点,用深度优先遍历和广度优先遍历标记它所经过的顶点,并将顶点之间的边也写入到graph1.txt和graph2.txt中
附代码:
void Mark_DFS(Graph G,int p){//标记深度优先遍历的顶点信息
visited1[p] = 1;
flag[i] = p;
i++;
Arc *t = G.v[p].firstarc;
while(t != NULL){
if(visited1[t->adjvex] == 0)
Mark_DFS(G,t->adjvex);
t = t->nextarc;
}
}
void Print_DFS(Graph G){//输出深度优先遍历的顶点与两点间距离
fstream graph1;
graph1.open("graph1.txt",ios::out);
if(!graph1){
cout << "打开文件失败!" << endl;
exit(0);
}
for(int p = 0;p < i - 1;p ++){
graph1 << flag[p] << '\t';
graph1 << flag[p + 1] << '(' << G.v[flag[p]].firstarc->weight << ')'
<< '\t' << endl;
}
graph1.close();
}
void BFS(Graph G,int p){//广度优先遍历
fstream graph2;
graph2.open("graph2.txt",ios::out);
if(!graph2){
cout << "打开文件失败!" << endl;
exit(0);
}
LinkQueue Q;
InitQueue(Q);
EnQueue(Q,p);
visited1[p] = 1;
while(Q.front != Q.rear){
int flag = 0;
DeQueue(Q,p);
Arc *t = G.v[p].firstarc,*t1 = t;
while(t1 != NULL){
if(visited1[t1->adjvex] == 0){
graph2 << p << '\t';
flag = 1;
break;
}
t1 = t1->nextarc;
}
while(t != NULL){
if(visited1[t->adjvex] == 0){
EnQueue(Q,t->adjvex);
visited1[t->adjvex] = 1;
graph2 << t->adjvex << '(' << t->weight << ')' << '\t';
}
t = t->nextarc;
}
if(flag == 1)
graph2 << endl;
}
DestroyQueue(Q);
graph2.close();
}
任务四
给定一个顶点,输出最小生成树
用kruskal解决
代码如下:
void Kruskal(Graph G,Link *tree,Link *link){//最小生成树(Kruskal算法)
int *flag = new int[G.vexnum];
for (int i = 0; i < G.vexnum; i++)
flag[i] = i;
sort(link,link + G.arcnum,compare);
int num = 0;
for(int i = 0;i < G.arcnum;i ++){
int start = link[i].start;
int end = link[i].end;
if(flag[start] != flag[end]){
tree[num] = link[i];
num++;
int e = flag[end];
for(int j = 0;j < G.vexnum;j ++){
if(flag[j] == e)
flag[j] = flag[start];
}
if(num == G.vexnum - 1)
break;
}
}
for(int i = 0;i < num;i ++)
cout << tree[i].start << " "
<< tree[i].end << " "
<< tree[i].weight << " "
<< endl;
delete[] flag;
}
任务五
给出两个顶点,输出最短路径长度以及所经过的顶点
最短路径长度:
每次都是从起始顶点v出发,找与v有关的有向边<v,u>。找到<v, u>之后,找有向边<u,k>,如果通过绕过顶点u而使从v到k的路径更短的话,就要修改v到k的路径。即v->u>k的路径比v->k的路径更短
输出最短路径:
如果当前顶点不是起点,把此顶点加入到路径d(保存路径的数组)中,然后从path数组的对应位置(编号)找到此顶点上一个顶点,把顶点加入到路径d数组中,重复操作,直至向上找到起点,最后倒过来输出d数组
附代码:
void ShortestPath(Graph G,Link *link,int p1,int p2){//最短路径
int *dis = new int[G.vexnum]{0};
int path[G.vexnum] = {0};
int m = 0;
InitLink(G,link);
while(link[m].start != p1)
m++;
while(link[m].start == p1){
dis[link[m].end] = link[m].weight;
m++;
}
for(int i = 0;i < G.vexnum;i ++){
if(dis[i] == 0)
dis[i] = 99999;
}
for(int i = 0;i < G.vexnum;i ++){
if(i != p1 && dis[i] < 99999)
path[i] = p1;
else
path[i] = -1;
}
dis[p1] = 0;
visited1[p1] = 1;
for(int i = 0;i < G.vexnum - 1;i ++){
int min = 99999;
int u = p1;
for(int j = 0;j < G.vexnum;j ++){
if(visited1[j] == 0 && dis[j] < min){
min = dis[j];
u = j;
}
}
visited1[u] = 1;
for(int k = 0;k < G.vexnum;k ++){
int n = 0,weight;
while(link[n].start != u)
n++;
while(link[n].start == u && link[n].end != k)
n++;
if(link[n].start != u)
weight = 99999;
else
weight = link[n].weight;
if((visited1[k] == 0) && (weight < 99999) && (weight + dis[u]< dis[k])){
dis[k] = weight + dis[u];
path[k] = u;
}
}
}
int j,k;
int *d = new int[G.vexnum];
for(i = 0;i < G.vexnum;i ++){
if(i != p1){
j = i;
k = 0;
while(j != p1){
d[k++] = j;
j = path[j];
}
if(i != p2)
continue;
cout << "最短路径为:" << endl;
cout << p1;
while(k > 0)
cout << "->" << G.v[d[--k]].code;
cout << endl << "最短路径长度为:" << dis[i] << endl;
}
}
delete[] dis;
delete[] d;
}
结语
就像之前说的,这篇文章是边做边写的,很多地方都是粗制滥造的,改完代码之后还要再写一篇报告,就不在这里修改了(菜狗能力有限)。。。