图的基本运算及智能交通中的最佳路径问题
本实验代码已更新,因改动本文内容较大未上传,如果需要相关代码,记得私信博主或下方评论哦~
实验一:已知图的邻接矩阵结构定义,编写程序完成邻接矩阵的初始化、撤销、边的搜索、边的插入、删除等操作。
借鉴: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; }