题目 图的遍历
【问题描述】
1、掌握图的数据类型描述及特点。
2、对图分别采用邻接表和邻接矩阵表示,并进行深度遍历和广度遍历。
【实验内容】
该程序开始时是通过用户输入的图的数据文件(.txt)所在的路径来读取对应文件中的图的数据,以此来构建,进而调用遍历算法函数来对图进行遍历,如果文件不存在或路径不正确,程序将会报告错误并终止;
本程序读取的文件的格式是.txt文件(大家也可以手工输入数据),其中存储的数据组成如下:
第一行:M N, M是图中结点的个数,N是图中弧的条数
第二行:D,D是1或0, 1表示该图是一个有向图,
0表示该图是一个无向图
第三行:M个互不相同的字符,代表每个结点的字符数据
接下来的N行,每行有2个字母P1、P2,对于有向图,表示存在一条从P1到P2的有向边;
对于无向图,表示在P1和P2之间存在一条边;
程序中采用的图的存储结构为邻接表和邻接矩阵,对于有向图,一条弧将存储一个弧结点,对于无向图,一条弧将存储两条弧结点;
代码:
#include<iostream> #include<fstream> #include<queue> #include<stdlib.h> using namespace std; const int DefaultVertices=30; //默认最大顶点数 const int maxWeight=100;//代表无穷大 template<class T,class E> struct Edge// 边的类定义 { int dest;//边的另一顶点位置 E cost;//权值 Edge<T,E> *link;//下一条边链指针 Edge(){} Edge(int num,E weight):dest(num),cost(weight),link(NULL){} bool operator != (Edge<T,E>& R)const//判边不等否 { return (dest!=R.dest)?true:false; } }; template<class T,class E> struct Vertex//顶点类定义 { T data;//顶点 Edge<T,E> *adj;//头指针 }; template<class T,class E> class Graphlnk { public: Graphlnk(int sz=DefaultVertices); ~Graphlnk(); T getValue(int i)//取顶点中值 { return (i>=0&&i<numVertices)?NodeTable[i].data:0; } E getWeight(int v1,int v2);//返回权值 bool insertVertex(const T& vertex);//插入顶点 bool insertEdge(int v1,int v2,E cost);//插入无向边 bool insertEdge1(int v1,int v2,E weight);//插入有向边 int getFirstNeighbor(int v);//取第一个邻接顶点 int getNextNeighbor(int v,int w);//下一邻接顶点 void Build(Graphlnk<T,E>& G);//建立图 void Output(Graphlnk<T,E>& G);//输出图 void DFS(Graphlnk<T,E>& G,const T& v);//深度优先遍历 void BFS(Graphlnk<T,E>& G,const T& v);//广度优先遍历 void Readfile(Graphlnk<T,E>& G); int NumberOfVertices(){return numVertices;}//返回当前顶点数 int NumberOfEdges(){return numEdges;}//返回当前边数 private: int maxVertices;//图中最大顶点数 int numEdges;//当前边数 int numVertices;//当前顶点数 void DFS(Graphlnk<T,E>& G,int v,bool visited[]); Vertex<T,E> *NodeTable;//顶点表 int getVertexPos(const T vertices)//给出顶点vertex在图中的位置 { for(int i=0;i<numVertices;i++) if(NodeTable[i].data==vertices) return i; return -1; } }; template<class T,class E> Graphlnk<T,E>::Graphlnk(int sz) {//构造函数建立一个空邻接表 maxVertices=sz; numVertices=0; numEdges=0; NodeTable=new Vertex<T,E>[maxVertices];//创建顶点表数组 if(NodeTable==NULL) { cerr<<"存储分配错!"<<endl; exit(1); } for(int i=0;i<maxVertices;i++) NodeTable[i].adj=NULL; }; template<class T,class E> Graphlnk<T,E>::~Graphlnk() { for(int i=0;i<numVertices;i++)//删除各边链表中的结点 { Edge<T,E> *p=NodeTable[i].adj; while(p!=NULL) { NodeTable[i].adj=p->link; delete p; p=NodeTable[i].adj; } } delete []NodeTable;//删除顶点表数组 }; template<class T,class E> int Graphlnk<T,E>::getFirstNeighbor(int v) { if(v!=-1) { Edge<T,E> *p=NodeTable[v].adj; if(p!=NULL) return p->dest; } return -1; }; template<class T,class E> int Graphlnk<T,E>::getNextNeighbor(int v,int w) { if(v!=-1) { Edge<T,E> *p=NodeTable[v].adj; while(p!=NULL&&p->dest!=w) p=p->link; if(p!=NULL&&p->link!=NULL) return p->link->dest; } return -1; }; template<class T,class E> E Graphlnk<T,E>::getWeight(int v1,int v2) { if(v1!=-1&&v2!=-1) { Edge<T,E> *p=NodeTable[v1].adj; while(p!=NULL&&p->dest!=v2) p=p->link; if(p!=NULL) return p->cost; } return -1; }; template<class T,class E> bool Graphlnk<T,E>::insertVertex(const T& vertex) { if(numVertices==maxVertices) return false; NodeTable[numVertices].data=vertex; numVertices++; return true; }; template<class T,class E> bool Graphlnk<T,E>::insertEdge(int v1,int v2,E weight)//插入无向边 { if(v1>=0&&v1<numVertices&&v2>=0&&v2<numVertices) { Edge<T,E> *q,*p=NodeTable[v1].adj; while(p!=NULL&&p->dest!=v2) p=p->link; if(p!=NULL) return false; p=new Edge<T,E>; q=new Edge<T,E>; p->dest=v2; p->cost=weight; p->link=NodeTable[v1].adj; NodeTable[v1].adj=p; q->dest=v1; q->cost=weight; q->link=NodeTable[v2].adj; NodeTable[v2].adj=q; numEdges++; return true; } return -1; }; template<class T,class E> bool Graphlnk<T,E>::insertEdge1(int v1,int v2,E weight)//插入有向边 { if(v1>=0&&v1<numVertices&&v2>=0&&v2<numVertices) { Edge<T,E> *p=NodeTable[v1].adj; while(p!=NULL&&p->dest!=v2) p=p->link; if(p!=NULL) return false; p=new Edge<T,E>; p->dest=v2; p->cost=weight; p->link=NodeTable[v1].adj; NodeTable[v1].adj=p; numEdges++; return true; } return -1; }; template<class T,class E> void Graphlnk<T,E>::Build(Graphlnk<T,E>& G) { int i,j,k,n,m,d; T e1,e2; E weight; cout<<"输入顶点数:"<<endl; cin>>n; cout<<"边数:"<<endl; cin>>m;//顶点,边数 cout<<"1、无向图;2、有向图"; cin>>d; cout<<"顶点表数据:"<<endl; for(i=0;i<n;i++)//建立顶点表数据 { cout<<"第"<<i+1<<"个:"; cin>>e1; G.insertVertex(e1); } i=0; while(i<m) { cout<<"输入第"<<i+1<<"个端点信息(e1,e2,w):"; cin>>e1>>e2>>weight;//输入端点信息 j=G.getVertexPos(e1); k=G.getVertexPos(e2);//查顶点号 if(j==-1||k==-1) cout<<"边两端点信息有误,重新输入!"<<endl; else { if(d==0) G.insertEdge(j,k,weight); else G.insertEdge1(j,k,weight); i++; } } }; template<class T,class E> void Graphlnk<T,E>::Output(Graphlnk<T,E>& G) { int i,j,n,m; T e1,e2; E w; n=G.NumberOfVertices(); m=G.NumberOfEdges(); cout<<n<<","<<m<<endl; for(i=0;i<n;i++) for(j=i+1;j<n;j++) { w=G.getWeight(i,j); if(w>0&&w<maxWeight) { e1=G.getValue(i); e2=G.getValue(j); cout<<"("<<e1<<","<<e2<<","<<w<<")"<<endl; } } }; template<class T,class E> void Graphlnk<T,E>::DFS(Graphlnk<T,E>& G,const T& v)//深度优先遍历 { int i,loc,n=G.NumberOfVertices(); bool *visited=new bool[n]; for(i=0;i<n;i++) visited[i]=false; loc=G.getVertexPos(v); DFS(G,loc,visited); delete []visited; }; template<class T,class E> void Graphlnk<T,E>::DFS(Graphlnk<T,E>& G,int v,bool visited[]) { cout<<G.getValue(v)<<" "; visited[v]=true; int w=G.getFirstNeighbor(v); while(w!=-1) { if(visited[w]==false) DFS(G,w,visited); w=G.getNextNeighbor(v,w); } }; template<class T,class E> void Graphlnk<T,E>::BFS(Graphlnk<T,E>& G,const T& v)//广度优先遍历 { int i,w,n=G.NumberOfVertices(); bool *visited=new bool[n]; for(i=0;i<n;i++) visited[i]=false; int loc=G.getVertexPos(v); cout<<G.getValue(loc)<<" "; visited[loc]=true; queue<int>Q; Q.push(loc); while(!Q.empty()) { loc=Q.front(); Q.pop(); w=G.getFirstNeighbor(loc); while(w!=-1) { if(visited[w]==false) { cout<<G.getValue(w)<<" "; visited[w]=true; Q.push(w); } w=G.getNextNeighbor(loc,w); } } delete []visited; }; template<class T,class E> void Graphlnk<T,E>::Readfile(Graphlnk<T,E>& G)//从文件中读取图 { ifstream on("graph.txt"); if(!on) { cerr<<"Can't open the graph.txt!"<<endl; exit(1); } else { cout<<"Read the file successfully!"<<endl; } int i,j,k,n,m,d; T e1,e2; E weight; on>>n>>m;//顶点,边数 on>>d; for(i=0;i<n;i++)//建立顶点表数据 { on>>e1; G.insertVertex(e1); } i=0; while(i<m) { on>>e1>>e2>>weight;//输入端点信息 j=G.getVertexPos(e1); k=G.getVertexPos(e2);//查顶点号 if(d==0) G.insertEdge(j,k,weight); else G.insertEdge1(j,k,weight); i++; } on.close(); } void main() { int choice=0; char ch;//遍历的顶点 Graphlnk<char,int>G; while(choice!=6) { cout<<endl; cout<<" 图的遍历 "<<endl; cout<<"1、文件建立图:"<<endl; cout<<"2、输入建立图:"<<endl; cout<<"3、图的广度遍历:"<<endl; cout<<"4、图的深度遍历:"<<endl; cout<<"5、输出图:"<<endl; cout<<"6、退出!"<<endl; cout<<"输入要选择的选项:"<<endl; cin>>choice; switch(choice) { case 1:G.Readfile(G);break; case 2:G.Build(G);break; case 3: { cout<<"开始遍历的顶点:"; cin>>ch; G.BFS(G,ch); break; } case 4: { cout<<"开始遍历的顶点:"; cin>>ch; G.DFS(G,ch); break; } case 5:G.Output(G);break; default:exit(1);break; } } }
心得:
图的遍历,一个不算难得算法,拖了很久,终于做完了。可惜是在数据结构期末考试后,期末有一道是广度遍历的题目,现在明白了,可是考试时不会写...一开始看到这个实验,心里就觉得很难,不想做,这就是我这个人的最大问题。其实做起来和以前的实验差不多,都是搬书上的代码,然后加上一些自己的理解,再经过调试就做出来了。总的来说,2天的时间,算是比较短了。
有向图、无向图、深度遍历、广度遍历,都不算难,关键是理解代码的运行,切记不要死记硬背!
C++,从树上得到的知识不经过运行,永远都不会理解的,要多上机,才会理解代码!加油!