在这篇文章中,我们要讨论一下关于图的知识点:
1.图的存储方式——邻接矩阵存储和邻接表存储
*邻接矩阵存储code如下所示
#include <iostream>
using namespace std;
template <class T>
class Graph
{
protected:
int n,e; //e是边数,n是节点数
public:
virtual bool GInsert(int u,int v,T w)=0; //图中在节点u和v之间插入一条权值为w的边
virtual bool GRemove(int u,int v)=0; //删除图中节点u和v之间的边
virtual bool GExist(int u,int v) const=0; //判断图中节点u和v之间是否存在路径
};
template <class T>
class MGraph:public Graph<T>
{
protected: //这里用protected权限是为了未来可能存在的继承该MGraph的类可以访问
T **a; //二维数组指针指向图的邻接矩阵
public:
MGraph(int mSize);
~MGraph();
bool GInsert(int u,int v,T w);
bool GRemove(int u,int v);
bool GExist(int u, int v)const;
};
template <class T>
MGraph<T>::MGraph(int mSize) //邻接矩阵是n*n的大小,即有n个节点
{
n=mSize;
a=new T* [n]; //申请一个大小为n的T*指针类型数组空间,每个空间存储一个T指针
for(int i=0;i<n;i++) //初始化邻接矩阵
{
a[i]=new T [n]; //i个数组空间中的指针a[i]分别指向一段n连续的T类型数组空间
for(int j=0;j<n;j++)
{
a[i][j]=-1; //赋值-1,表明i和j节点之间没有边
}
a[i][i]=0; //节点无自回路
}
}
template <class T>
MGraph<T>::~MGraph()
{
for(int i=0;i<n;i++)
delete []a[i];
delete []a;
}
template <class T>
bool MGraph<T>::GInsert(int u, int v, T w)
{
if(u<0||v<0||u>n-1||v>n-1)
{
cout<<"节点值越界"<<endl;
return false;
}
if(a[u][v]!=-1)
{
cout<<"边已经存在"<<endl;
return false;
}
a[u][v]=w;
e++;
return true;
}
template <class T>
bool MGraph<T>::GRemove(int u,int v)
{
if(u<0||v<0||u>n-1||v>n-1)
{
cout<<"节点值越界"<<endl;
return false;
}
if(a[u][v]==-1)
{
cout<<"要删除的边不存在"<<endl;
return false;
}
a[u][v]==-1;
e--;
return true;
}
template <class T>
bool MGraph<T>::GExist(int u, int v) const
{
if(u<0||v<0||u>n-1||v>n-1)
{
cout<<"GExit:节点越界"<<endl;
return false;
}
if(a[u][v]==-1)
{
cout<<"GExit:边不存在"<<endl;
return false;
}
cout<<"GExit:边存在"<<endl;
return true;
}
int main()
{
MGraph<int> a(4);
a.GInsert(0,1,1);
a.GExist(0,2);
return 0;
}
*邻接表类似于一个哈希表,邻接表存储方式的图code如下所示
#include <iostream>
using namespace std;
template <class T> class LGraph;
template <class T>
class LNode //邻接表节点类
{
private:
int adjVex; //节点号
T w; //边权值
LNode<T> *nextArc; //节点指针
public:
LNode(int vertex,T weight,LNode<T> *next)
{
adjVex=vertex;
w=weight;
nextArc=next;
}
friend class LGraph<T>; //友元类LGraph可以访问类LNode中的private成员
};
template <class T>
class LGraph //邻接表类
{
private:
LNode<T> **a; //邻接表二维指针
int n; //图的节点数
int e; //图的边数
public:
LGraph(int mSize);
~LGraph();
bool LExist(int u,int v);
bool LInsert(int u,int v,T w);
bool LRemove(int u,int v);
};
template<class T>
LGraph<T>::LGraph(int mSize) //初始化邻接表
{
n=mSize;
a=new LNode<T>* [n];
for(int i=0;i<n;i++)
{
a[i]=NULL;
}
}
template<class T>
LGraph<T>::~LGraph()
{
for(int i=0;i<n;i++)
{
delete [] a[i];
}
delete []a;
}
template<class T>
bool LGraph<T>::LExist(int u,int v)
{
if(u<0||v<0||u>n-1||v>n-1)
{
cout<<"LExist:节点号越界"<<'\n';
return false;
}
LNode<T> *p=a[u];
while(p)
{
if(p->adjVex==v)
{
cout<<"LExist:存在"<<u<<"-"<<v<<"边"<<'\n';
return true;
}
p=p->nextArc;
}
if(!p)
return false;
}
template<class T>
bool LGraph<T>::LInsert(int u,int v,T w)
{
if(u<0||v<0||u>n-1||v>n-1)
{
cout<<"LInsert:节点号越界"<<'\n';
return false;
}
if(LExist(u,v))
{
cout<<"LInsert:已经存在"<<u<<"-"<<v<<"边"<<'\n';
return false;
}
LNode<T> *p=new LNode<T>(v,w,a[u]);
a[u]=p;
e++;
return true;
}
template<class T>
bool LGraph<T>::LRemove(int u,int v)
{
if(u<0||v<0||u>n-1||v>n-1)
{
cout<<"LRemove:节点号越界"<<'\n';
return false;
}
if(!LExist(u,v))
{
cout<<"LRemove:不存在"<<u<<"-"<<v<<"边"<<'\n';
return false;
}
LNode<T> *p=a[u],*q=NULL;
while(p&&p->adjVex!=v)
{
q=p;
p=p->nextArc;
}
if(!p)
return false;
if(q)
q->nextArc=p->nextArc;
else
a[u]=p->nextArc;
delete p;
e--;
return true;
}
2.图的深度优先遍历(DFS),这里我们讨论以邻接表形式存储的图如何进行深度优先遍历。这里采用了递归法,大致思路如下
(1)访问任意节点v,并对v打上已经访问过的标记
(2)依次从v的未访问邻接点出发,深度优先搜索图
#include "lgraph.cpp"
//图的搜索
template <class T>
class ExtLGraph:public LGraph<T>
{
private:
void DFS(int v,bool *visited); //从v节点出发深度优先搜索,visited是标志位数组指针
//void BFS();
public:
ExtLGraph(int mSize):LGraph<T>(mSize){} //调用父类的构造函数来初始化子类构造函数
void DFS(); //深度优先遍历
//void BFS(); //宽度优先遍历
};
template <class T>
void ExtLGraph<T>::DFS()
{
bool *visited=new bool [n]; //创建标志位数组
for(int i=0;i<n;i++)
visited[i]=false; //所有标志位至false,表明未访问过
for(int i=0;i<n;i++)
if(!visited[i]) //未访问过的节点进行深度优先搜索递归算法
DFS(i,visited);
delete [] visited;
}
template <class T>
void ExtLGraph<T>::DFS(int v,bool *visited)
{
visited[v]=true; //v节点标志位至为true
cout<<" "<<v; //打印输出访问到的节点v
LNode<T> *p=a[v];
while(p)
{
if(!visited[p->adjVex])
DFS(p->adjVex,visited);
p=p->nextArc;
}
}
注意:由于ExtLGraph类是LGraph的子类,由于友元类无法继承的原因,在ExtLGraph类中无法访问LNode类中的private成员,因此LNode类中应该将ExtLGraph类设置为友元类!!!