zoukankan      html  css  js  c++  java
  • C++ 图的实现

    图可以说是算法与数据结构中十分重要的一个部分,然而对于图的实现,还是有一点点繁琐,今天参考清华大学出版社《数据结构》一书进行了一些回顾,记录于此。

    本文并不对基本概念进行过多探讨,而着眼于实现。基于对途中边集的存储有邻接矩阵以及邻接表两种主要形式。本文将着重实现三个类: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)
    	virtual int getVertexPos(T vertex)=0;
    
    protected:
    	int maxVertices;
    	int numEdges;
    	int numVertices;
    	
    };
    


    • 基于邻接表实现边集存储的派生子类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();
    	int getVertexPos( T vertex){
    		for (int i = 0;i<numVertices;i++)
    		{
    			if(NodeTable[i].data == vertex)
    				return i;
    		}
    		return -1; //找不到就返回-1
    	}
    
    private:
    	Vertex<T,E> * NodeTable; //顶点表
    	
    };
    
    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>
    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!=-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) //获得v的邻接顶点w的下一个邻接顶点
    {
    	if(v!=-1 )
    	{
    		Edge<T,E> *p = NodeTable[v].adj;
    		while(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;	
    	numVertices++;
    	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  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 weight)	//插入一条边,若边已存在,或参数不合理,返回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 = 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 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
    		{
    			if(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>
    {
    
    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();
    	int getVertexPos(T vertex)									//给出顶点在图中的位置
    	{
    		for(int i=0;i<numVertices;i++)
    			if(VerticesList[i]==vertex)return i;
    		return -1;												//找不到返回-1
    	}
    private:
    	T *VerticesList;											//顶点表
    	E * *Edge;													//邻接矩阵
    	
    };
    
    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
    		{
    			if(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"
    #include "Graphlnk.h"
    
    void test_Graphmtx()
    {
    	char ch1,ch2;
    	int weight;
    	Graphmtx<char,int> g(30);
    	g.inputGraph();		//构造图
    	g.outputGraph();	//显示图
    
    	cout<<"顶点数和边数:"<<g.NumberOfVertices()<<" "<<g.NumberOfEdges()<<endl;
    	cout<<"查看第一个邻接顶点:";
    	cin>>ch1;
    	cout<<g.getValue(g.getFirstNeighbor(g.getVertexPos(ch1)))<<endl;
    	cout<<"查看后一个邻接顶点:";
    	cin>>ch1>>ch2;
    	cout<<g.getValue(g.getNextNeighbor(g.getVertexPos(ch1),g.getVertexPos(ch2)))<<endl;
    
    	cout<<"插入顶点:";
    	cin >>ch1;
    	g.insertVertex(ch1);  //插入点
    	cout<<"插入边:"<<endl;
    	cin >>ch1>>ch2>>weight;
    	g.insertEdge(g.getVertexPos(ch1),g.getVertexPos(ch2),weight);//插入边
    	g.outputGraph();
    
    	cout<<"删除边:";
    	cin >>ch1>>ch2;
    	g.removeEdge(g.getVertexPos(ch1),g.getVertexPos(ch2)); //删除边
    
    	cout<<"删除点:";
    	cin>>ch1;
    	g.removeVertex(g.getVertexPos(ch1));
    	g.outputGraph();
    	
    }
    
    void test_Graphlnk()
    {
    	cout<<"-----------Grapglnk Test-----------"<<endl;
    	char ch1,ch2;
    	int weight;
    	Graphmtx<char,int> g(30);
    	g.inputGraph();		//构造图
    	g.outputGraph();	//显示图
    
    	cout<<"顶点数和边数:"<<g.NumberOfVertices()<<" "<<g.NumberOfEdges()<<endl;
    	cout<<"查看第一个邻接顶点:";
    	cin>>ch1;
    	cout<<g.getValue(g.getFirstNeighbor(g.getVertexPos(ch1)))<<endl;
    	cout<<"查看后一个邻接顶点:";
    	cin>>ch1>>ch2;
    	cout<<g.getValue(g.getNextNeighbor(g.getVertexPos(ch1),g.getVertexPos(ch2)))<<endl;
    
    	cout<<"插入顶点:";
    	cin >>ch1;
    	g.insertVertex(ch1);  //插入点
    	cout<<"插入边:"<<endl;
    	cin >>ch1>>ch2>>weight;
    	g.insertEdge(g.getVertexPos(ch1),g.getVertexPos(ch2),weight);//插入边
    	g.outputGraph();
    
    	cout<<"删除边:";
    	cin >>ch1>>ch2;
    	g.removeEdge(g.getVertexPos(ch1),g.getVertexPos(ch2)); //删除边
    
    	cout<<"删除点:";
    	cin>>ch1;
    	g.removeVertex(g.getVertexPos(ch1));
    	g.outputGraph();
    }

    //Filename : main.cpp
    #include "Graphmtx.h"
    extern void test_Graphmtx();
    extern void test_Graphlnk();
    
    int main()
    {
    	
    	test_Graphmtx();
    	test_Graphlnk();
    	system("pause");
    	return 0;
    }


    经测试,以上代码可以正确运行。




  • 相关阅读:
    【转载】eclipse常用插件在线安装地址或下载地址
    【转载】Eclipse快捷键 10个最有用的快捷键
    Visual Studio(C#)快捷键与Eclipse(JAVA)快捷键对比
    【转载】Eclipse 的快捷键以及文档注释、多行注释的快捷键
    Spring 事务配置的几种方式
    Linux下 执行程序
    转:C# 通过委托更新UI(异步加载)
    C#AutoResetEvent和ManualResetEvent的区别
    转 RMI、RPC、SOAP通信技术介绍及比对
    转泛型
  • 原文地址:https://www.cnblogs.com/f8master/p/3826075.html
Copyright © 2011-2022 走看看