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)

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();
}

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

时间: 2024-10-29 08:11:11

C++ 图的实现的相关文章

利用filter实时切换big5和gb2312,以及gb2312的简繁体

IEEE Spectrum 杂志发布了一年一度的编程语言排行榜,这也是他们发布的第四届编程语言 Top 榜. 据介绍,IEEE Spectrum 的排序是来自 10 个重要线上数据源的综合,例如 Stack Overflow.Twitter.Reddit.IEEE Xplore.GitHub.CareerBuilder 等,对 48 种语言进行排行. 与其他排行榜不同的是,IEEE Spectrum 可以让读者自己选择参数组合时的权重,得到不同的排序结果.考虑到典型的 Spectrum 读者需求

俑烟汲的诿樟透磺勒秤窗mvus

IEEE Spectrum 杂志发布了一年一度的编程语言排行榜,这也是他们发布的第四届编程语言 Top 榜. 据介绍,IEEE Spectrum 的排序是来自 10 个重要线上数据源的综合,例如 Stack Overflow.Twitter.Reddit.IEEE Xplore.GitHub.CareerBuilder 等,对 48 种语言进行排行. 与其他排行榜不同的是,IEEE Spectrum 可以让读者自己选择参数组合时的权重,得到不同的排序结果.考虑到典型的 Spectrum 读者需求

时序图与状态图(Rose) - Windows XP经典软件系列

最近开始了自己高级数据结构之旅,在这次旅行中,我将持续把一些高级的数据结构从理论到编码都过一遍,同时通过博客形式分享出来,希望大家指出不足之处! 二叉排序树是一种动态排序的数据结构,支持插入.删除.查找等操作,且平均时间复杂度为O(log(N)),但是普通二叉排序树不能保证树退化为一颗分支的情况,此时最坏情况下的时间复杂度为O(N).此时,平衡二叉树的产生了.平衡二叉树是一种动态调整平衡的数据结构,但理想的平衡二叉树很难,于是人们使用AVL.红黑树.Treap.伸展树等来替代平衡二叉树,这些数据

类图(Rose) - Windows XP经典软件系列

最近开始了自己高级数据结构之旅,在这次旅行中,我将持续把一些高级的数据结构从理论到编码都过一遍,同时通过博客形式分享出来,希望大家指出不足之处! 二叉排序树是一种动态排序的数据结构,支持插入.删除.查找等操作,且平均时间复杂度为O(log(N)),但是普通二叉排序树不能保证树退化为一颗分支的情况,此时最坏情况下的时间复杂度为O(N).此时,平衡二叉树的产生了.平衡二叉树是一种动态调整平衡的数据结构,但理想的平衡二叉树很难,于是人们使用AVL.红黑树.Treap.伸展树等来替代平衡二叉树,这些数据

一张图掌握移动Web前端所有技术(大前端、工程化、预编译、自动化)

你要的移动web前端都在这里! 大前端方向:移动Web前端.Native客户端.Node.js. 大前端框架:React.Vue.js.Koa 跨终端技术:HTML 5.CSS 3.JavaScript 跨平台框架:React Native.Cordova 前端工程化:Grunt.Gulp.Webpack 前端预编译:Babel.Sass.Less 自动化测试:Jasmine.Mocha.Karma 一图在手,应有尽有! 更多信息参考:https://item.jd.com/12170351.h

Java企业微信开发_08_JSSDK多图上传

一.本节要点 1.1可信域名 所有的JS接口只能在企业微信应用的可信域名下调用(包括子域名),可在企业微信的管理后台“我的应用”里设置应用可信域名.这个域名必须要通过ICP备案,不然jssdk会配置失败 1.2JS-SDK使用权限签名算法 1.2.1 签名生成规则如下: (1)参与签名的字段包括: noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) . (2)对所有待签名参数按照字段名的ASCII

以JPanel为基础实现一个图相框

代码: import java.awt.Graphics; import javax.swing.ImageIcon; import javax.swing.JPanel; public class Picture extends JPanel { private static final long serialVersionUID = -4437881316229152596L; private ImageIcon icon; public Picture(java.net.URL imgUR

jQuery----无缝轮播图

1.效果 2.html代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <link rel="stylesheet" type="text/css" href="css/iconfont.css"> <link rel="stylesh

UESTC30-最短路-Floyd最短路、spfa+链式前向星建图

最短路 Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的T-shirt.但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗? Input 输入包括多组数据. 每组数据第一行是两个整数NN ,MM (N≤100N≤100 ,M≤10000M≤1000

Android自己定义控件之轮播图控件

背景 近期要做一个轮播图的效果.网上看了几篇文章.基本上都能找到实现,效果还挺不错,可是在写的时候感觉每次都要单独去又一次在Activity里写一堆代码.于是自己封装了一下.这里仅仅是做了下封装成一个控件,不必每次反复写代码了. 效果图 实现分析 轮播图的功能就是实现左右滑动的广告.图片信息展示,那我们就用ViewPager来实现,由于考虑到用户体验,我们还须要在以下加一个指示器来标示滑动到了第几张轮播图.指示器我们能够用一个线性布局来依据要展示的轮播图设置显示的View,我们要做这种一个控件没