题目 图的遍历
【问题描述】
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++,从树上得到的知识不经过运行,永远都不会理解的,要多上机,才会理解代码!加油!