zoukankan      html  css  js  c++  java
  • 图的基本运算及智能交通中的最佳路径问题

    本实验代码已更新,因改动本文内容较大未上传,如果需要相关代码,记得私信博主或下方评论哦~

    实验一:已知图的邻接矩阵结构定义,编写程序完成邻接矩阵的初始化、撤销、边的搜索、边的插入、删除等操作。

    借鉴:https://blog.csdn.net/qq_37360631/article/details/80482044

    邻接矩阵.h

    #include<stdio.h>
    #include<stdlib.h>
    typedef int ElemType;
    typedef struct{
        ElemType **a;   //邻接矩阵 
        int n;      //图的顶点数 
        int e;      //图当前的边数 
        ElemType noEdge;    //两顶点无边时的值 
    }mGraph;
    //初始化,nSize顶点数,noEdgeValue无边的值 
    int Init(mGraph *mg,int nSize,ElemType noEdgeValue); 
    //撤销操作,撤销的时候顺序不能变,先释放指针数组就会找不到数组指针啦 
    int Destory(mGraph *mg);
    //边的搜索操作,判断边在不在图中 
    int Exist(mGraph *mg,int u,int v) ;
    //边的插入
    int Insert(mGraph *mg,int u,int v,ElemType w);
    //边的删除
    int Remove(mGraph *mg,int u,int v);

    邻接矩阵.cpp

    #include"邻接矩阵.h"
    #include<iostream.h>
    //初始化,nSize顶点数,noEdgeValue无边的值 
    int Init(mGraph *mg,int nSize,ElemType noEdgeValue){
        int i,j;
        mg->n=nSize;
        mg->e=0;        //初始化没有边 
        mg->noEdge=noEdgeValue;
        mg->a=(ElemType**)malloc(nSize*sizeof(ElemType*));  //生成长度为n的一维数组指针
        if(!mg->a) return 0;
        for(i=0;i<mg->n;i++){
            mg->a[i]=(ElemType*)malloc(nSize*sizeof(ElemType));
            for(j=0;j<mg->n;j++) mg->a[i][j]=mg->noEdge;
            mg->a[i][i]=0;                                  //自回路设置为0 
        } 
        return 1;
    } 
    //撤销操作
    int Destory(mGraph *mg){
        int i;
        for(i=0;i<mg->n;i++) free(mg->a[i]);    //释放n个一维数组存储空间 
        free(mg->a);                                //释放n个一维指针数组的存储空间 
        return 1;
    } 
    //边的搜索操作,判断边在不在图中 
    int Exist(mGraph *mg,int u,int v){
        if(u<0||v<0||u>mg->n-1||v>mg->n-1||u==v||mg->a[u][v]==mg->noEdge){
            cout<<"这条边不存在."<<endl;
            return 0;
        }
        cout<<"这条边存在."<<endl; 
        cout<<"这条边的长度为:"<<mg->a[u][v]<<endl;
        return 1;
    } 
    //边的插入
    int Insert(mGraph *mg,int u,int v,ElemType w){
        if(u<0||v<0||u>mg->n-1||v>mg->n-1||u==v) return 0;
        if(mg->a[u][v]!=mg->noEdge) {
            cout<<"该边已存在."<<endl;
            return 0; 
        }
        mg->a[u][v]=w;
        mg->e++;
        return 1;
    } 
    
    //边的删除
    int Remove(mGraph *mg,int u,int v){
        if(u<0||v<0||u>mg->n-1||v>mg->n-1||u==v)
        return 0;
        if(mg->a[u][v]==mg->noEdge) return 0;   //删除的边不存在
        mg->a[u][v]=mg->noEdge;
        mg->e--;
        return 1; 
    } 
    
    int main(){
        mGraph mg;
        int n,n1,u,v,w,a,b;
    //  fangxin();
    //  fx();
        cout<<"请输入顶点的个数:"<<endl;
        cin>>n;         
        Init(&mg,n,0);
        cout<<"请输入插入边的个数:"<<endl;
        cin>>n1;
        cout<<"请输入插入边的起点(顶点计算从0开始)、终点和边权:"<<endl;
        for(int i=0;i<n;i++){
            cout<<""<<i+1<<""<<endl;
            cin>>u>>v>>w;
            Insert(&mg,u,v,w);
        }
        cout<<"请输入查找的边的两个端点:"<<endl;
        cin>>a>>b;
        Exist(&mg,a,b);
        Destory(&mg);
        Remove(&mg,0,1);
        return 0;
    } 

    分析:    1.首先这些代码创建的是一个带权的有向图;

        2.插入边的时候,是以边带点的创建;

        3.查询后,程序会死掉。

    将实验一、二、三、四结合起来:

    实验二:编写程序,实现图的深度和宽度优先遍历。

    实验三:编写程序完成邻接表的的初始化、撤销、边的搜索、插入、删除等操作。

    实验四:编写程序,实现图的深度和宽度优先遍历。

    #include<iostream.h>
    const int INFTY=2147483640;
    enum Result{Underflow,Duplicate,ERROR,OK,NotPresent};//枚举了所有可能的结果
    
    template<class T>
    class Graph               //定义抽象类
    {
        public:
            virtual Result Insert(int u,int v,T &w)=0;//创建边
            virtual Result Remove(int u,int v)=0;//删除边
            virtual bool Exist(int u,int v)const=0;//判断边是否存在
        protected:
            int n;
            int e;
    };
    
    template<class T>
    class SeqQueue
    {
        public:
            SeqQueue(int mSize);
            ~SeqQueue()
            {
                delete []q;
            }
            bool isEmpty() const{return rear==front;}
            bool isFull() const{return (rear+1)%maxSize==front;}
            bool Front(T &x)const;
            bool EnQueue(T x);
            bool DeQueue();
            void Clear(){front=rear=0;}
        private:
            int front,rear;
            int maxSize;
            T *q;
    };
    template<class T>
    SeqQueue<T>::SeqQueue(int mSize)  //构造函数
    {
        maxSize=mSize;
        q=new T[maxSize];
        front=rear=0;
    }
    template<class T>
    bool SeqQueue<T>::Front(T &x)const   //取出队首元素
    {
        if(isEmpty())
        {
            cout<<"是空的"<<endl;
            return false;
        }
        x=q[(front+1)%maxSize];
        return true;
    }
    template<class T>
    bool SeqQueue<T>::EnQueue(T x)     //在队尾插入元素x
    {
        if(isFull())
        {
            cout<<"已经满了哦"<<endl;
            return false;
        }
        q[rear=(rear+1)%maxSize]=x;
        return true;
    }
    template<class T>
    bool SeqQueue<T>::DeQueue() //删除队首元素
    {
        if(isEmpty())
        {
            cout<<"Underflow"<<endl;
    
            return false;
        }
        front=(front+1)%maxSize;
        return true;
    }
    
    template<class T>
    class mGraph:public Graph<T>    //邻接矩阵类
    {
        public:
            mGraph();
            mGraph(int mSize,const T& noedg);
            ~mGraph();
            Result Insert(int u,int v,T &w);//创建边
            Result Remove(int u,int v);//删除边
            bool Exist(int u,int v)const;//判断边是否存在
            void DFS();//深度优先遍历
            void BFS();//宽度优先遍历
        private:
            T **a;
            T noEdge;
            void DFS(int v,bool *visited);
            void BFS(int v,bool *visited);
    };
    
    template<class T>
    mGraph<T>::mGraph(int mSize,const T& noedg)   //邻接矩阵的构造函数
    {
        n=mSize;
        e=0;
        noEdge=noedg;
        a=new T*[n];
        for(int i=0;i<n;i++)
        {
            a[i]=new T[n];
            for(int j=0;j<n;j++)
                a[i][j]=noEdge;
            a[i][j]=0;
        }
    }
    template<class T>
    mGraph<T>::~mGraph()                        //邻接矩阵的析构函数
    {
        for(int i=0;i<n;i++)   
            delete []a[i];
        delete []a;
    }
    template<class T>
    Result mGraph<T>::Insert(int u,int v,T &w)  //插入
    {
        if(u<0||v<0||u>n-1||u==v)
            return ERROR;
        if(a[u][v]!=noEdge)
            return Duplicate;
        a[u][v]=w;
        e++;
        return OK;
    }
    template<class T>
    Result mGraph<T>::Remove(int u,int v)       //删除
    {
        if(u<0||v<0||u>n-1||u==v)
            return ERROR;
        if(a[u][v]==noEdge)
            return NotPresent;
        a[u][v]=noEdge;
        e--;
        return OK;
    }
    template<class T>
    bool mGraph<T>::Exist(int u,int v)const      //判断边是否存在
    {
        if(u<0||v<0||u>n-1||u==v||a[u][v]==noEdge)
            return false;
        return true;
    }
    template<class T>
    void mGraph<T>::DFS()
    {
        bool *visited=new bool[n];
        for(int i=0;i<n;i++)
            visited[i]=false;                  //预设所有结点均未被访问过
        for(i=0;i<n;i++)
            if(!visited[i])
                DFS(i,visited);
        delete []visited;
    }
    template<class T>
    void mGraph<T>::DFS(int v,bool *visited)
    {
        visited[v]=true;
        cout<<" "<<v;
        for(int i=0;i<n;i++)
            if(a[v][i]!=noEdge&&a[v][i]!=0&&!visited[i])
                DFS(i,visited);
    }
    template<class T>
    void mGraph<T>::BFS()
    {
        bool *visited=new bool[n];
        for(int i=0;i<n;i++)
            visited[i]=false;                  //预设所有结点均未被访问过
        for(i=0;i<n;i++)
            if(!visited[i])
                BFS(i,visited);
        delete []visited;
    }
    template<class T>
    void mGraph<T>::BFS(int v,bool *visited)
    {
        SeqQueue<int>q(n);
        visited[v]=true;
        cout<<" "<<v;
        q.EnQueue(v);
        while(!q.isEmpty())
        {
            q.Front(v);
            q.DeQueue();
            for(int i=0;i<n;i++)
                if(a[v][i]!=noEdge&&a[v][i]!=0&&!visited[i])
                {
                    visited[i]=true;
                    cout<<" "<<i;
                    q.EnQueue(i);
                }
        }
    }
    template<class T>    //结点类
    class ENode
    {
        public:
            ENode(){nextArc==NULL;}
            ENode *nextArc;
            ENode(int v,T wei,ENode *next)
            {
                adjVex=v;
                w=wei;
                nextArc=next;
            }
            int adjVex;
            T w;
            
    };
    template<class T>
    class LGraph:public Graph<T>       //邻接表类
    {
        public:
            LGraph(int mSize);
            ~LGraph();
            Result Insert(int u,int v,T &w);//创建边
            Result Remove(int u,int v);//删除边
            bool Exist(int u,int v)const;//判断边是否存在
        protected:
            ENode<T>**a;
    };
    template<class T>
    LGraph<T>::LGraph(int mSize)  //邻接表的构造函数
    {
        n=mSize;
        e=0;
        a=new ENode<T> *[n];
        for(int i=0;i<n;i++)
            a[i]=NULL;
    }
    
    template<class T>
    LGraph<T>::~LGraph()                        //邻接表的析构函数
    {
        ENode<T>*p,*q;
        for(int i=0;i<n;i++)   
        {
            p=a[i];
            q=p;
            while(p)
            {
                p=p->nextArc;
                delete q;
                q=p;
            }
        }
        delete []a;
    }
    template<class T>
    bool LGraph<T>::Exist(int u,int v)const      //判断边是否存在
    {
        if(u<0||v<0||u>n-1||u==v)
            return false;
        ENode<T>*p=a[u];
        while(p&&p->adjVex!=v)
            p=p->nextArc;
        if(!p)
            return false;
        else return true;
    }
    template<class T>
    Result LGraph<T>::Insert(int u,int v,T&w)       //插入
    {
        if(u<0||v<0||u>n-1||u==v)
            return ERROR;
        if(Exist(u,v))
            return Duplicate;
        ENode<T>*p=new ENode<T>(v,w,a[u]);
        a[u]=p;
        e++;
        return OK;
    }
    
    template<class T>
    Result LGraph<T>::Remove(int u,int v)       //删除
    {
        if(u<0||v<0||u>n-1||u==v)
            return ERROR;
        ENode<T>*p=a[u],*q;
        q=NULL;
        while(p&&p->adjVex!=v)
        {
            q=p;
            p=p->nextArc;
        }
        if(!p)
            return NotPresent;
        if(q)
            q->nextArc=p->nextArc;
        else
            a[u]=p->nextArc;
        delete p;
        e--;
        return OK;
    }
     int main()  //主函数
     {
         int n,g;
         cout<<"请输入元素的个数:";
         cin>>n;
         mGraph<int>A(n,INFTY);
         LGraph<int>B(n); 
         cout<<"请输入边的条数:";
         cin>>g;
         int *a=new int[g];
         int *b=new int[g];
         int *w=new int[g];
         for(int i=0;i<g;i++)
         {
             cout<< "请输入边及权值:";
             cin>>a[i]>>b[i]>>w[i];
             A.Insert(a[i],b[i],w[i]);
             B.Insert(a[i],b[i],w[i]);
         }
         cout<<"该图的深度优先遍历为:"<<endl;
         A.DFS();
         cout<<endl;
         cout<<"该图的宽度优先遍历为:"<<endl;
         A.BFS();
         cout<<endl;
         cout<<"请输入要搜索的边:";
         int c,d;
         cin>> c>>d;
         if(A.Exist(c,d))
             cout<<"邻接矩阵中该边存在!"<<endl;
         else
             cout<<"邻接矩阵中该边不存在!"<<endl;
         if(B.Exist(c,d))
             cout<<"邻接表中该边存在!"<<endl;
         else
             cout<<"邻接表中该边不存在!"<<endl;
         cout<<"请输入要删除的边:";
         int e,f;
         cin>>e>>f;
         if(A.Remove(e,f)==OK)
             cout<<"邻接矩阵中删除该边成功!"<<endl;
         else if(A.Remove(e,f)==NotPresent)
             cout<<"邻接矩阵中该边不存在!"<<endl;
         else
             cout<<"输入错误!"<<endl;
         if(B.Remove(e,f)==OK)
             cout<<"邻接表中删除该边成功!"<<endl;
         else if(B.Remove(e,f)==NotPresent)
             cout<<"邻接表中该边不存在!"<<endl;
         else
             cout<<"邻接表中输入错误!"<<endl;
         cout<<"删除该边后该图的深度优先遍历为:"<<endl;
         A.DFS();
         cout<<endl;
         cout<<"删除该边后该图的宽度优先遍历为:"<<endl;
         A.BFS();
         cout<<endl;
         return 0;
     }

    注:代码同时使用了邻接矩阵和邻接表,产生一定的错误。可以分开。

    实验五:编写程序,实现智能交通中的最佳路径选择问题:设有n个地点,编号为0→n-1,m条路径的起点、终点和代价由用户输入提供,使用邻接表为存储结构,寻找最佳路径方案(如花费时间最少,路径长度最短,交通费用最少等问题任选其一即可)

    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <queue>
    using namespace std;
    #define INF 0xfffff    //权值上限
    #define maxn 110  //最大顶点个数
    int n;       //顶点个数
    
    struct arcnode  //边结点
    {
        int vertex;     //与表头结点相邻的顶点编号
        int weight;     //连接两顶点的边的权值
        arcnode * next; //指向下一相邻接点
        arcnode() {}
        arcnode(int v,int w):vertex(v),weight(w),next(NULL) {}
    };
    
    struct vernode      //顶点结点,为每一条邻接表的表头结点
    {
        int vex;    //当前定点编号
        arcnode * firarc;   //与该顶点相连的第一个顶点组成的边
    }Ver[maxn];
    
    void Init()  //建立图的邻接表需要先初始化,建立顶点结点
    {
        for(int i = 1; i <= n; i++)
        {
            Ver[i].vex = i;
            Ver[i].firarc = NULL;
        }
    }
    
    void Insert(int a, int b, int w)  //尾插法,插入以a为起点,b为终点,权为w的边,效率不如头插,但是可以去重边
    {
        arcnode * q = new arcnode(b, w);
        if(Ver[a].firarc == NULL)
            Ver[a].firarc = q;
        else
        {
            arcnode * p = Ver[a].firarc;
            if(p->vertex == b)
            {
                if(p->weight > w)
                    p->weight = w;
                return ;
            }
            while(p->next != NULL)
            {
                if(p->next->vertex == b)
                {
                    if(p->next->weight > w)
                        p->next->weight = w;
                    return ;
                }
                p = p->next;
            }
            p->next = q;
        }
    }
    void Insert2(int a, int b, int w)   //头插法,效率更高,但不能去重边
    {
        arcnode * q = new arcnode(b, w);
        if(Ver[a].firarc == NULL)
            Ver[a].firarc = q;
        else
        {
            arcnode * p = Ver[a].firarc;
            q->next = p;
            Ver[a].firarc = q;
        }
    }
    struct node     //顶点节点,保存id和到源顶点的估算距离,优先队列需要的类型
    {
        int id;     //源顶点id和估算距离
        int w;
        friend bool operator<(node a, node b)   //因要实现最小堆,按升序排列,因而需要重载运算符,重定义优先级,以小为先
        {
            return a.w > b.w;
        }
    };
    
    int parent[maxn];   //每个顶点的父亲节点,可以用于还原最短路径树
    bool visited[maxn]; //用于判断顶点是否已经在最短路径树中,或者说是否已找到最短路径
    node d[maxn];      //源点到每个顶点估算距离,最后结果为源点到所有顶点的最短路。
    priority_queue<node> q; //优先队列stl实现
    void Dijkstra(int s)    //Dijkstra算法,传入源顶点
    {
        for(int i = 1; i <= n; i++) //初始化
        {
            d[i].id = i;
            d[i].w = INF;           //估算距离置INF
            parent[i] = -1;         //每个顶点都无父亲节点
            visited[i] = false;     //都未找到最短路
        }
        d[s].w = 0;                 //源点到源点最短路权值为0
        q.push(d[s]);               //压入队列中
        while(!q.empty())           //算法的核心,队列空说明完成了操作
        {
            node cd = q.top();      //取最小估算距离顶点
            q.pop();
            int u = cd.id;
            if(visited[u])   
                continue;
            visited[u] = true;
            arcnode * p = Ver[u].firarc;
            //松弛操作
            while(p != NULL)    //找所有与他相邻的顶点,进行松弛操作,更新估算距离,压入队列。
            {
                int v = p->vertex;
                if(!visited[v] && d[v].w > d[u].w+p->weight)
                {
                    d[v].w = d[u].w+p->weight;
                    parent[v] = u;
                    q.push(d[v]);
                }
                p = p->next;
            }
        }
    }
    
    int main()
    {
        int m, a, b, c, st, ed;
        printf("请输入地点数和路径数:
    ");
        scanf("%d%d", &n, &m);
        printf("请输入路径以及路径长度(a, b, c)
    ");
        Init();     //计算前必须初始化
        while(m--)
        {
            scanf("%d%d%d", &a, &b, &c);
            Insert2(a, b, c);   //无向图注意存储两条边
            Insert2(b, a, c);
        }
        printf("请输入起点和终点:
    ");
        scanf("%d%d", &st, &ed);
        Dijkstra(st);
        if(d[ed].w != INF)
            printf("%d到%d最短路径长度为:%d
    ", st,ed,d[ed].w);
        else
            printf("不存在从地点%d到地点%d的最短路径。
    ", st, ed);
        return 0;
    }
  • 相关阅读:
    C++ Builder string相互转换(转)
    Delphi中ComPort通信中的数据处理(转)
    GPRS管理与创建APN拨号连接(转)
    Delphi MaskEdit用法(转)
    GPRS DTU概念及DTU的工作原理(转)
    TIdTCPClient 详解
    GPRS的工作原理、主要特点
    Android studio快捷键
    HDU 1255 覆盖的面积(线段树+扫描线)
    Rescue
  • 原文地址:https://www.cnblogs.com/WittPeng/p/9141794.html
Copyright © 2011-2022 走看看