今天正式在博客园开通博客,特此将今天学习的内容记录一下,看一看博客园的博客效果。
图可以说是算法与数据结构中十分重要的一个部分,然而对于图的实现,还是有一点点繁琐,今天参考清华大学出版社《数据结构》一书进行了一些回顾,记录于此。
本文并不对基本概念进行过多探讨,而着眼于实现。基于对途中边集的存储有邻接矩阵以及邻接表两种主要形式。本文将着重实现三个类:Graph基类,包含大量的virtual函数以待在派生类中实现;Graph的派生类Graphmtx(邻接矩阵实现图的存储)、Graphlnk(邻接表实现图的存储)。并通过简单的test对以上实现加以测试。废话不多说,直接贴代码,代码中加了比较详细的注释说明。
- 基类Graph定义:
//FileName : Graph.h
#pragma once
#include<iostream>
using namespace std;#define INF 100000 //表示正无穷
const int DefaultVertices = 30;template<class T,class E>
class Graph
{
public:
static const E maxWeight = INF;
Graph(int size = DefaultVertices){};
~Graph(){};
bool GraphEmpty()const //检查为空
{
if (numEdges ==0 )return true;
else return false;
}
bool GraphFull()const //检查为满
{
if(numVertices==maxVertices ||numEdges==maxVertices*(maxVertices-1)/2)
return true;
else
return false;
}
int NumberOfVertices(){return numVertices;} //返回当前顶点数
int NumberOfEdges(){return numEdges;} //返回当前边数
virtual T getValue(int i)=0; //取顶点i的值,i不合理返回0
virtual E getWeight(int v1,int v2)=0; //取边(v1,v2)的权值
virtual int getFirstNeighbor(int v)=0; //取顶点v的第一个邻接顶点
virtual int getNextNeighbor(int v,int w)=0; //取邻接顶点w的下一个邻接顶点
virtual bool insertVertex(const T& vertex)=0; //插入一个顶点vertex
virtual bool insertEdge(int v1, int v2,E cost)=0;//插入边(v1,v2),权值cost
virtual bool removeVertex(int v)=0; //删除顶点v和所有与之关联的边
virtual bool removeEdge(int v1,int v2)=0; //删除边(v1,v2)protected:
int maxVertices;
int numEdges;
int numVertices;
virtual int getVertexPos(T vertex)=0;};
- 基于邻接表实现边集存储的派生子类Graphlnk定义及实现:
//Filename : Grapglnk.h
#include "Graph.h"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 Graph<T,E>
{
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);
int getFirstNeighbor(int v);
int getNextNeighbor(int v,int w);
bool insertVertex(const T& vertex);
bool insertEdge(int v1, int v2,E cost);
bool removeVertex(int v);
bool removeEdge(int v1,int v2);
void inputGraph();
void outputGraph();
private:
Vertex<T,E> * NodeTable; //顶点表
int getVertexPos(const T vertex){
for (int i = 0;i<numVertices;i++)
if(NodeTable[i].data == Vertex)
return i;
return -1; //找不到就返回-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<maxVertices;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>
E Graphlnk<T,E>::getWeight(int v1,int v2) //返回边(v1,v2)的权重,边不存在则返回0
{
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 0;
}template<class T,class E>
int Graphlnk<T,E>::getFirstNeighbor(int v) //获得v的第一个邻接顶点,找不到则返回-1
{
if(v>=0 && v<numVertices)
{
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) //获得v的邻接顶点w的下一个邻接顶点
{
if(v1>=0 && v1<numVertices && v2>=0 && v2<numVertices)
{
Edge<T,E> *p = NodeTable[v1].adj;
if(p!=NULL && p->dest != w) //寻找邻接顶点w
p = p->link;
if(p!=NULL && p->link!=NULL) //找到w且存在下一个邻接顶带你
return p->link->dest;
}
return -1:
}template<class T ,class E>
bool Graphlnk<T,E>::insertVertex(const T& vertex) //插入点
{
if(numVertices == maxVertices) return false; //图已满,插入失败
NodeTable[numVertices++].data = vertex; //
return true;
}template<class T ,class E>
bool Graphlnk<T,E>::removeVertex(int v) //删除点
{
if(numVertices ==1 || v<0 ||v>=numVertices) return false;
Edge<T,E> *p ,*s ,*t;
int i, k;
while(NodeTable[v].adj != NULL) //删除该顶点,以及与之邻接的顶点中的记录
{
p = NodeTable[v].adj;
k = p->dest;
s = NodeTable[k].adj; //以找对称存放的边节点
t = NULL;
while (s!=NULL && s->dest!= v) //在对称点的邻接表里面找v,删除掉
{
t = s;
s = s->link;
}
if(s!=NULL)
{
if(t==null) //第一个邻接顶点就是v
NodeTable[k].adj = s->link;
else
t->link = s->link;
delete s;
}
NodeTable[v].adj = p->link;
delete p;
numEdges --;
}
numVertices--;
NodeTable[v].data = NodeTable[numVertices].data;
p = NodeTable[v].adj = NodeTable[numVertices].adj;
while(p!=NULL)
{
s = NodeTable[p->dest].adj;
while(s!=NULL)
{
if(s->dest == numVertices)
{
s->dest = v;
break;
}
else
s = s->link;
}
p = p->link
}
return true;
}template<class T ,class E>
bool Graphlnk<T,E>::insertEdge(int v1, int v2,E cost) //插入一条边,若边已存在,或参数不合理,返回false
{
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)//找到该边
{
cout<<"该边已经存在,插入失败!"<<endl;
return false;
}
p = new Edge<T,E>;
q = new Edge<T,E>;
p->dest = v2;
p->cost = cost;
p->link = NodeTable[v1].adj;
NodeTable[v1].adj = p; //插入到邻接表表头
q->dest = v1;
q->cost = cost;
q->link = NodeTable[v2].adj;
NodeTable[v2].adj = p;
numEdges ++;
return true;
}
return false;
}template<class T, class E>
bool Graphlnk<T,E>::removeEdge(int v1,int v2)
{
if(v1 >=0 && v1< numVertices && v2>=0 && v2< numVertices)
{
Edge<T,E> *p = NodeTable[v1].adj, *q = NULL ;*s = p;
while(p!=NULL && p->dest!= v2) //先找该边
{
q = p;
p = p->link;
}
if(p!=NULL) //找到该边
{
if(p==s)//第一个节点就找到
NodeTable[v1].adj = p->link;
else
q->link = p->link;
delete p;
}
else return false; //找不到边
p = NodeTable[v2].adj; q= NULL; s = p;
while(p!=NULL && p->dest!=v1)
{
q = p;
p = p->link;
}
if(p==s)
NodeTable[v2].adj = p->link;
else
q->link = p->link;
delete p;
return true;
}
return false;
}template<class T ,class E>
void Graphlnk<T,E>::inputGraph()
{
//通过从输入流对象in输入n的顶点和e条五项边的信息建立邻接矩阵表示的图G。邻接矩阵初始化工作在构造函数完成
int i,j,k,m,n;
T e1,e2;
E weight;
cout<<"请输入顶点数和边数(空格隔开):"<<endl;
cin >> n >> m; //输入点数n的边数m
cout<<"请依次输入顶点:"<<endl;
for(i=0;i<n;i++)//输入顶点,建立顶点表
{
cin>>e1;
this->insertVertex(e1);
//G.insertVertex(e1);
}
cout<<"请依次输入边,形如 v1 v2 weight :"<<endl;
i=0;
while(i<m)
{
cin >> e1>>e2>>weight;
j = this->getVertexPos(e1);//查顶点号
k = this->getVertexPos(e2);
if(j==-1 || k==-1)
{
cout<<"边两端点信息有误,重新输入!"<<endl;
}
else
{
this->insertEdge(j,k,weight);
i++;
}
}}
template<class T ,class E>
void Graphlnk<T,E>::outputGraph()
{
//输出图的所有顶点和边信息
int i,j,n,m;
T e1,e2;
E weight;
n = this->NumberOfVertices(); //点数
m = this->NumberOfEdges(); //边数
cout<<"顶点数的边数为:";
cout<<n<<","<<m<<endl; //输出点数和边数
cout<<"各边依次为:"<<endl;
for(i=0;i<n;i++)
{
for(j=i+1;j<n;j++)
{
weight = this->getWeight(i,j);
if(weight>0 && weight< maxWeight)
{
e1 = this->getValue(i);
e2 = this->getValue(j);
cout<<"("<<e1<<","<<e2<<","<<weight<<")"<<endl;
}}
}}
- 基于邻接矩阵实现边集存储的派生子类Graphmtx定义及实现:
//Filename : Graphmtc.h#include "Graph.h"
#include<iostream>
using namespace std;template<class T,class E>
class Graphmtx: public Graph<T,E>
{
//friend istream &operator>>(istream &in,Graphmtx<T,E> &G); //输入
//friend ostream &operator<<(ostream &out,Graphmtx<T,E> &G); //输出
public:
Graphmtx(int sz=DefaultVertices); //构造
~Graphmtx() //析构
{
delete []VerticesList;
delete []Edge;
}
T getValue(int i) //取顶点i的值,若i不合理返回NULL
{
if(i>=0 && i<numVertices) return VerticesList[i];
else return NULL;
}
E getWeight(int v1,int v2) //取边(v1,v2)的权值,不合理返回0
{
if(v1!=-1 && v2!=-1)
return Edge[v1][v2];
else
return 0;
}
int getFirstNeighbor(int v);
int getNextNeighbor(int v,int w);
bool insertVertex(const T& vertex);
bool insertEdge(int v1, int v2,E cost);
bool removeVertex(int v);
bool removeEdge(int v1,int v2);
void inputGraph();
void outputGraph();
private:
T *VerticesList; //顶点表
E * *Edge; //邻接矩阵
int getVertexPos(T vertex) //给出顶点在图中的位置
{
for(int i=0;i<numVertices;i++)
if(VerticesList[i]==vertex)return i;
return -1; //找不到返回-1
}
};template<class T ,class E>
Graphmtx<T,E>::Graphmtx(int sz) //构造函数
{
maxVertices = sz;
numVertices = 0;
numEdges = 0;
int i,j;
VerticesList = new T[maxVertices];
Edge = new E *[maxVertices];
for (i=0;i<maxVertices;i++)
Edge[i]= new E[maxVertices];
for(i=0;i<maxVertices;i++)
for(j=0;j<maxVertices;j++)
Edge[i][j] = (i==j) ? 0 :maxWeight;
}template<class T,class E>
int Graphmtx<T,E>::getFirstNeighbor(int v)//返回v的第一个邻接顶点的位置
{
if(v!=-1)
{
for(int col =0;col<maxVertices;col++)
if(Edge[v][col]>0 && Edge[v][col]<maxWeight)
return col;
}
return -1;
}template<class T,class E>
int Graphmtx<T,E>::getNextNeighbor(int v,int w)//返回v的邻接顶点w的下一个邻接顶点
{
if (v!=-1 && w!=-1)
{
for (int col =w+1;col<maxVertices;col++)
{
if(Edge[v][col]>0 &&Edge[v][col]<maxWeight)
return col;
}
}
return -1;
}template<class T ,class E>
bool Graphmtx<T,E>::insertVertex(const T& vertex) //插入一个顶点
{
if(numVertices == maxVertices)return false; //顶点表已满,返回false
VerticesList[numVertices++]=vertex;
return true;
}template<class T ,class E>
bool Graphmtx<T,E>::insertEdge(int v1, int v2,E cost)//插入一条边
{
if(v1>-1 && v1<numVertices && v2>-1 && v2<numVertices) //检查条件
{
if( Edge[v1][v2]==maxWeight)
{
Edge[v1][v2]=Edge[v2][v1] = cost;
numEdges++;
return true;
}
else
{
cout<<"该边已存在,添加失败"<<endl;
return false;
}
}
else return false;
}template<class T,class E>
bool Graphmtx<T,E>::removeVertex(int v) //删除一个顶点
{
if(v<0 ||v>numVertices) return false; //v不在图中
if(numVertices==1) return false; //只剩一个顶点,不删除
int i,j;
VerticesList[v]=VerticesList[numVertices-1]; //顶点表中删除
for( i=0;i<numVertices;i++) //边数调整
if(Edge[i][v]>0 && Edge[i][v]<maxWeight)
numEdges--;
for(i=0;i<numVertices;i++)
Edge[i][v]=Edge[i][numVertices-1];
numVertices--; //顶点数调整
for(j=0;j<numVertices;j--)
Edge[v][j]=Edge[numVertices][j];
return true;
}template<class T,class E>
bool Graphmtx<T,E>::removeEdge(int v1,int v2) //删除边
{
if(v1>-1 && v1<numVertices && v2>-1 &&v2<numVertices && Edge[v1][v2]>0 && Edge[v1][v2]<maxWeight)
{
Edge[v1][v2] = Edge[v1][v2] = maxWeight;
numEdges--;
return true;
}
else return false;
};template<class T ,class E>
void Graphmtx<T,E>::inputGraph()
{
//通过从输入流对象in输入n的顶点和e条五项边的信息建立邻接矩阵表示的图G。邻接矩阵初始化工作在构造函数完成
int i,j,k,m,n;
T e1,e2;
E weight;
cout<<"请输入顶点数和边数(空格隔开):"<<endl;
cin >> n >> m; //输入点数n的边数m
cout<<"请依次输入顶点:"<<endl;
for(i=0;i<n;i++)//输入顶点,建立顶点表
{
cin>>e1;
this->insertVertex(e1);
//G.insertVertex(e1);
}
cout<<"请依次输入边,形如 v1 v2 weight :"<<endl;
i=0;
while(i<m)
{
cin >> e1>>e2>>weight;
j = this->getVertexPos(e1);//查顶点号
k = this->getVertexPos(e2);
if(j==-1 || k==-1)
{
cout<<"边两端点信息有误,重新输入!"<<endl;
}
else
{
this->insertEdge(j,k,weight);
i++;
}
}}
template<class T ,class E>
void Graphmtx<T,E>::outputGraph()
{
//输出图的所有顶点和边信息
int i,j,n,m;
T e1,e2;
E weight;
n = this->NumberOfVertices(); //点数
m = this->NumberOfEdges(); //边数
cout<<"顶点数的边数为:";
cout<<n<<","<<m<<endl; //输出点数和边数
cout<<"各边依次为:"<<endl;
for(i=0;i<n;i++)
{
for(j=i+1;j<n;j++)
{
weight = this->getWeight(i,j);
if(weight>0 && weight< maxWeight)
{
e1 = this->getValue(i);
e2 = this->getValue(j);
cout<<"("<<e1<<","<<e2<<","<<weight<<")"<<endl;
}}
}}
- 编写测试程序加以简单测试:
//Filename: test.cpp
#include "Graphmtx.h"
void test_Graphmtx()
{
Graphmtx<char,int> g(30);
g.inputGraph();
g.outputGraph();
system("pause");
}void test_Graphlnk()
{
Graphmtx<char,int> g(30);
g.inputGraph();
g.outputGraph();
system("pause");
}
//Filename : main.cpp
#include "Graphmtx.h"
extern void test_Graphmtx();
extern void test_Graphlnk();void main()
{
//test_Graphmtx();
test_Graphlnk();
}
经测试,以上代码可以正确运行。