图的基本运算及智能交通中的最佳路径问题

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

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

借鉴: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("请输入地点数和路径数:\n");
    scanf("%d%d", &n, &m);
    printf("请输入路径以及路径长度(a, b, c)\n");
    Init();     //计算前必须初始化
    while(m--)
    {
        scanf("%d%d%d", &a, &b, &c);
        Insert2(a, b, c);   //无向图注意存储两条边
        Insert2(b, a, c);
    }
    printf("请输入起点和终点:\n");
    scanf("%d%d", &st, &ed);
    Dijkstra(st);
    if(d[ed].w != INF)
        printf("%d到%d最短路径长度为:%d\n", st,ed,d[ed].w);
    else
        printf("不存在从地点%d到地点%d的最短路径。\n", st, ed);
    return 0;
}

 

posted @ 2018-06-06 10:18  WittPeng  阅读(1702)  评论(1编辑  收藏  举报