C++实现有向权图的基本操作,界面友好,操作方便,运行流畅

本文介绍了一个C++实现的有向权图,包括创建、展示、添加顶点和边、删除操作、查询边权值、修改边权值、查找两点间所有简单路等功能。程序具有良好的用户交互性,但存在代码冗长、复用率低、使用goto语句等问题,期待改进。
摘要由CSDN通过智能技术生成

Ⅰ.功能:

          1.创建图

          2.展示全图

          3.添加顶点

          4.添加边

          5.删除顶点

          6.删除边

          7.查看指定边权值

          8.修改指定边权值

          9.输出两点间的所有简单路及路径对应权值

          10.销毁图

           ps:关于9,如果不存在任何简单路,输出“不存在任何简单路”。简单路:不含重复顶点的路。如

               1 2 3 4 5是简单路,1 2 3 1 4  5不是简单路。可以想见,对于大多数问题,非简单路是没有

              研究意义的。如从北京到上海的路线,多次经过同一地点的路线是没有研究价值的。

 Ⅱ.不足:

            1.代码冗长,复用率低。原因是有些操作基本相同但又有细微不同(如goto mark6;goto  

               mark7;),难以封装成一个函数。在此请求大神给出好的解决办法。

            2.类的成员函数的实现也写在了头文件中,使头文件过长。原因是前几次代码使用了模板,(看 

              过在下前几篇博文的读者应该知道)而用模板的话,就要求要么将类的成员函数的实现和类放

              在一起,要么放在main函数中(尽管听有人说《C++Primer》中说不这样也行,但在下实测的

              结果是——必须这样)。在下写习惯了,写完后才发现对于此次代码,将类的成员函数的实现

              另放一个文件好。但代码已经写完,就暂时不改了,万一再改出bug来,没准又得调半天,这里

              和大家说一声,求原谅。

            3.使用了goto语句(前几次也一样),有人对goto严格禁止,但依在下愚见,小范围内使用还是

               可以的,再说,在下也是迫不得已,实在没有更好的办法了。还是那句话,求大神指教。

            4.时空复杂性较高,特别是时间复杂性,懂这方面的读者一看在下的代码便知。主要是顶点和边

              都是用链表实现的,动不动就要遍历,但话说回来,至今在下还没找到比用链表实现更好的方

              法,还请高人指教。

 Ⅲ.优点:

             1.程序在多处用了控制,保证了程序的健壮性和友好,可重复操作,体验良好。

             2.仅提供Operate()函数一个接口,较大限度地实现了封装。

  Ⅳ.博主推荐

            1.找两点间所有简单路并输出路径总权值有一定思维量和技巧性,有兴趣的读者可自行分析相

              关代码,体会其思想。此代码为本人原创(当然不排除读者在其它地方见到相似代码,尽管在

              下未参考任何书籍或网络资源)。

  Ⅴ.代码:

//.h文件

#ifndef GRAPH_H
#define GRAPH_H

#include<iostream>
#include<iomanip>
using namespace std;

struct Edge       //边结构体
{
    int egnum;    //结点编号
    int weight;   //权值
    Edge* enext;  //下一边结点
    Edge(int enm, int wgt, Edge* enxt);
};
Edge::Edge(int enm, int wgt, Edge* enxt)   //边结构体构造函数
{
    egnum = enm;
    weight = wgt;
    enext = enxt;
}

struct Vertices      //顶点结构体
{
    int number;      //结点编号
    Edge* state;       //顶点状态,用于标记顶点是否被遍历
    Edge* eghead;    //边链表头结点
    Vertices* vnext; //下一顶点结点
    Vertices(int num,Edge* sta, Edge* eghed, Vertices* vnxt);
};
Vertices::Vertices(int num,Edge* sta,Edge* eghed, struct Vertices* vnxt)   //顶点结构体构造函数
{
    number = num;
    state = sta;
    eghead = eghed;
    vnext = vnxt;
}

class Graph               //图类
{
private:
    int  vsize;           //记录顶点个数
    int  esize;           //记录边个数
    Vertices* vhead;      //顶点链表头指针
    Vertices* vtail;      //顶点链表尾指针
public:
    Graph();
    ~Graph();
    void Operate();       //封装操作
private:
    Vertices* Revads(const int num); //返回某顶点地址,同时用于检测某顶点是否存在
    int Creat();   //创建图
    Edge* IfEdge(const int vfrom, const int vto);  //查看两顶点间是否存在边
    bool Addvt();  //添加顶点
    int Addeg();  //添加边
    void ShowAll(); //展示全图
    int Getweit(const int vfrom, const int vto);   //查看指定边权值
    bool FinRoad(const int vfrom, const int vto);    //查找两点间所有简单路
    void Destory();  //销毁图
};
Graph::Graph()            //图类构造函数
{
    vsize = 0;
    esize = 0;
    vhead = NULL;
    vtail = NULL;
}

Graph::~Graph()           //图类析构函数
{
    Destory();
}
Vertices* Graph::Revads(const int num)                //返回某顶点地址,同时用于检测某顶点是否存在
{
    Vertices* pt = vhead;   bool flag = false;
    while (pt)
    {
        if (pt->number == num){ flag = true; break; }
        pt = pt->vnext;
    }
    if (flag)return pt;
    else return NULL;
}

Edge* Graph::IfEdge(const int vfrom, const int vto)         //查看两顶点间是否存在边
{
    Vertices* pt = Revads(vfrom);
    Edge* pte = pt->eghead;
    while (pte)
    {
        if (pte->egnum == vto)
        {
            return pte;
        }
        pte = pte->enext;
    }
    return NULL;
}

bool Graph::Addvt()                     //添加顶点
{
    cout << "请您输入要添加顶点的个数,程序将自动从当前图中顶点的最大编号往下顺次为新添加顶点编号:" << endl;
    int sum; cin >> sum;
    int itnum = vtail->number + 1;
    int begin = itnum;
    for (int i = 0; i < sum;i++)
    {
        vtail->vnext = new Vertices(itnum, NULL, NULL, NULL);
        if (!vtail->vnext){ cout << "申请空间失败!" << endl; return false; }
        vtail = vtail->vnext;
        itnum++;
        vsize++;
    }
    cout << "顶点添加成功!" << endl;
    cout << "新顶点编号从" << begin << "到" << itnum-1 << ",目前图中共有顶点" << vsize << "个。" << endl;
    cout << "当前新顶点为孤立顶点,建议您通过选项4添加指向新顶点的边和由新顶点发出的边。" << endl;
    return true;
}

int Graph::Addeg()                       //添加边
{
    cout << "提示:" << endl;
    cout << "输入时,请您依次输入边的起点编号、终点编号和边的权值,中间以空格相隔,例如:1 2 20。并且,请您务必输入正整数,";
    cout << "如果您未按规定输入,由此造成的一切后果,将由您个人承担,程序开发者概不负责!" << endl;
    bool sign = true, flag = true;
    int from, to, weit, choice, abandon,abandon1,abandon2;      //标签
    Vertices* pt=NULL;
    while (sign)
    {
        while (flag)
        {
            abandon = 0; abandon1 = 0; abandon2 = 0;       //只要重新输入,就要进行一次初始化
            cout << "请您依次输入边的起点、终点和权值:" << endl;
            cin>> from >> to >> weit;
            if (from == to)     //如果起、止点相同,不进行进一步检查,直接进行选择
            {
                cout << "起点编号与终点编号不得相同!重新输入请按1,放弃本次输入请按0:" << endl;
                cin >> choice;
                if (choice == 0){ abandon1 = 1; abandon = 1; }
                else  { abandon1 = 1; flag = false; }
            }
            if (!abandon1)  //如果起、止点不同,检查起、止点是否正确及两点间是否有边
            {
                pt = Revads(from);
                if (!pt)  //如果起点不存在,不进行进一步检查,直接进行选择
                {
                    cout << "您输入的起点不存在!重新输入请按1,放弃本次输入请按0:" << endl;
                    cin >> choice;
                    if (choice == 0){ abandon2 = 1; abandon = 1; }
                    else  { abandon2 = 1; flag = false; }
                }
                else  //如果起点存在,检查终点,若果终点不存在,不进行进一步检查,直接进行选择
                {
                    if (!Revads(to))
                    {
                        cout << "您输入的终点不存在!重新输入请按1,放弃本次输入请按0:" << endl;
                        cin >> choice;
                        if (choice == 0){ abandon2 = 1; abandon = 1; }
                        else { abandon2 = 1; flag = false; }
                    }
                }
                if (!abandon2)//如果两点输入正确且都存在,检查两点间是否有边,若无,进行选择
                {
                    Edge* ifedge = IfEdge(from, to);
                    if (ifedge)
                    {
                        cout << "从顶点" << from << "到顶点" <

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.深度优先遍历(Depth-First Traversal) 假设给定图G的初态是所有顶点均未曾访问过。在G中任选一顶点v为初始出发点(源点),则深度优先遍历可定义如下:首先访问出发点v,并将其标记为已访问过;然后依次从v出发搜索v的每个邻接点w。若w未曾访问过,则以w为新的出发点继续进行深度优先遍历,直至图中所有和源点v有径相通的顶点(亦称为从源点可达的顶点)均已被访问为止。若此时图中仍有未访问的顶点,则另选一个尚未访问的顶点作为新的源点重复上述过程,直至图中所有顶点均已被访问为止。 2.广度优先遍历 1)从图中某个顶点V0出发,并访问此顶点; 2)从V0出发,访问V0的各个未曾访问的邻接点W1,W2,…,Wk;然后,依次从W1,W2,…,Wk出发访问各自未被访问的邻接点; 3)重复步骤2,直到全部顶点都被访问为止。 3. prim算法 假设V是图中顶点的集合,E是图中边的集合,TE为最小生成树中的边的集合,则prim算法通过以下步骤可以得到最小生成树: 1)初始化:U={u 0},TE={f}。此步骤设立一个只有结点u 0的结点集U和一个空的边集TE作为最小生成树的初始形态,在随后的算法执行中,这个形态会不断的发生变化,直到得到最小生成树为止。 2)在所有u∈U,v∈V-U的边(u,v)∈E中,找一条权最小的边(u 0,v 0),将此边加进集合TE中,并将此边的非U中顶点加入U中。此步骤的功能是在边集E中找一条边,要求这条边满足以下条件:首先边的两个顶点要分别在顶点集合U和V-U中,其次边的权要最小。找到这条边以后,把这条边放到边集TE中,并把这条边上不在U中的那个顶点加入到U中。这一步骤在算法中应执行多次,每执行一次,集合TE和U都将发生变化,分别增加一条边和一个顶点,因此,TE和U是两个动态的集合,这一点在理解算法时要密切注意。 3)如果U=V,则算法结束;否则重复步骤2。可以把本步骤看成循环终止条件。我们可以算出当U=V时,步骤2共执行了n-1次(设n为图中顶点的数目),TE中也增加了n-1条边,这n-1条边就是需要求出的最小生成树的边。 4.Kruskal算法 假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值