2022nuaa数据结构课程设计--图编程

前言

做课设的时候思路总是不清晰,就想着边写博客边做题好了,随便把思路可以写下来。

题目要求

给出两个文件,小规模数据文件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;
}

任务五

给出两个顶点,输出最短路径长度以及所经过的顶点

最短路径长度:

Dijkstra算法

     每次都是从起始顶点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;    

结语

就像之前说的,这篇文章是边做边写的,很多地方都是粗制滥造的,改完代码之后还要再写一篇报告,就不在这里修改了(菜狗能力有限)。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值