- 需求分析
- 城市网络建设方案生成程序介绍
在当今时代,随着网络技术的发展,网络已经融入到人们生活的方方面面,给人们的生活带来了极大的便利,城市间的网络架设也成为了基础设施建设中的重要一环。在城市网络架设过程中,考虑到不同城市间的地理、距离等因素,存为多种成本不同的建设方案,为了方便网络建设人员在多种网络建设方案中快速找出最经济的架设方案,本网络建设方案生成程序应运而生。本程序要求系统界面友好、使用简单、能够处理网络的相关信息,生成最优架设方案、同时使得用户能方便的进行相关数据的增加、删除以及保存到文件的操作。
- 城市网络建设方案生成程序用户说明
本程序的用户可以在导入存有相关信息的数据文件后,获得最经济的架设方案,也可对已导入的数据文件进行管理。
3.城市网络建设方案生成程序功能介绍
图1.程序功能结构图
程序主要功能包括登录,导入数据文件,管理数据文件和生成最优架设方案,具体说明如下:
- 导入数据文件:将数据文件导入程序,在程序内生成相应的网络建设信息,以便执行后续操作。
- 管理数据文件:对程序当前的网络建设信息进行节点及网络的增加或删除,保存程序当前网络建设信息到文件。
- 生成最优方案:对程序当前网络建设信息进行分析,并生成最优架设方案。
4.城市网络建设方案生成程序功能分析
4.1导入数据文件
功能介绍:用户运行程序后从文件中读入图的数据信息,并据此生成图,如果失败则打印失败信息。
- 输入:
图的结点,两结点网络连线的权
- 输出:
菜单界面或失败信息
4.2管理数据文件-增加新的节点
功能介绍:用户输入新结点的信息后,把新结点增加到原来的图中。
- 输入:
需要增加的结点名称,该结点与其他结点的网络连线的权
- 输出:
节点添加成功或失败信息
4.3管理数据文件-增加一条网络连线
功能介绍:用户输入新网络连线的权与位置,把新网络连线增加到图中。
- 输入:
需要增加的网络连线的网络连线两端节点名称,该网络连线的权
- 输出:
网络添加成功或失败信息
4.4管理数据文件-删除一条网络连线
功能介绍:根据网络连线的信息,在图中删除该网络结点。
- 输入:
需要删除的网络连线两端结点名称
- 输出:
删除成功或失败信息
4.5管理数据文件-删除一个结点
功能介绍:根据结点名称,在图中删除该结点。
- 输入:
要删除的结点名称
- 输出:
删除成功或失败信息
4.6管理数据-得到最小生成树
功能介绍:根据图的信息,利用prim和kruscal算法得到最小生成树。
(1)输出:
由prim和kruscal算法得到最小生成树的结点,由prim和kruscal算法得到最小生成树关于两结点间的网络连线的权
4.7管理数据-保存
功能介绍:将图保存进数据文件中,以便下次使用时能重新查阅。
- 输出:
保存成功或失败信息
二、系统设计
1.系统结构设计
2.数据结构设计
2.1数据对象设计及数据结构实现
图对象:
抽象图类及其函数
template<typename Tv,typename Te>//顶点类型,边类型
class Graph//抽象图类
{
private:
int _n;//顶点总数
int _e;//边总数
void BFS(int v,int& clock);//广度优先遍历
void DFS(int v, int& clock);//深度优先遍历
void reset();
public:
//顶点操作接口
int& n() { return _n; } //返回点数
virtual int insert(Tv const& tv) = 0;//插入顶点,返回序号
virtual Tv remove(int v) = 0;//删除顶点,返回顶点信息
virtual Tv& vertex(int v) = 0;//返回顶点的数据
virtual int inDegree(int v) = 0;//返回顶点入度
virtual int outDegree(int v) = 0;//返回顶点出度
virtual int firstNbr(int v) = 0;//返回首个邻接顶点
virtual int nextNbr(int v,int u) = 0;//返回下一个邻接顶点
virtual VStatus& status(int v) = 0;//返回顶点的状态
virtual int& dTime(int v) = 0;//返回顶点的时间标签dTime
virtual int& fTime(int v) = 0;//返回顶点的时间标签fTime
virtual int& parent(int v) = 0;//返回顶点在遍历树中的父亲
virtual int& priority(int v) = 0;//返回顶点在遍历树中的优先级数
//边操作接口
int& e() { return _e; } //返回边数
virtual bool exists(int v, int u) = 0;//判断顶点v,u之间是否存在边
virtual void insert(int v, int u, Te const& te, int w) = 0;//在顶点v,u之间插入边
virtual Te remove(int v, int u) = 0;//在顶点v,u之间删除边
virtual EType& type(int v, int u) = 0;//返回顶点v,u之间的边类型
virtual Te& edge(int v, int u) = 0; // 返回顶点v, u之间的边数据
virtual int& weight(int v, int u) = 0;// 返回顶点v, u之间的边权重
//算法接口
void bfs(int s);
void dfs(int s);
};
图的邻接矩阵表示继承抽象图类及其函数:
数据结构:邻接矩阵
template<typename Tv,typename Te>
class GraphMatrix :public Graph<Tv,Te>
{
private:
Vector<Vertex<Tv>>V;//顶点集
Vector<Vector<Edge<Te>*>> E;//边集
template<typename T> void DFSEARCH(int v, T& t, int*& visit);//深度优先遍历
public:
GraphMatrix() { Graph<Tv,Te>::n() = 0; Graph<Tv, Te>::e() = 0; }
~GraphMatrix();
//顶点的基本操作
virtual Tv& vertex(int i) { return V[i].data; }//数据
virtual int inDegree(int i) { return V[i].inDegree; }
virtual int outDegree(int i) { return V[i].outDegree; }
virtual int firstNbr(int i) { return nextNbr(i, Graph<Tv, Te>::n()); }//返回首个邻接顶点
virtual int nextNbr(int i,int j);//返回下一个邻接顶点
virtual VStatus& status(int i) { return V[i].status; }//返回顶点的状态
virtual int& dTime(int i) { return V[i].dTime; }//返回顶点的时间标签dTime
virtual int& fTime(int i) { return V[i].fTime; }//返回顶点的时间标签fTime
virtual int& parent(int i) { return V[i].parent; }//返回顶点在遍历树中的父亲
virtual int& priority(int i) { return V[i].priority; }//返回顶点在遍历树中的优先级数
virtual int findVertex(const Tv & t);
//顶点的动态操作
virtual int insert(Tv const& vertex);//插入顶点,返回编号
virtual Tv remove(int i);//删除顶点,返回数据
//边的基本操作
virtual bool exists(int v, int u);//判断顶点v,u之间是否存在边
virtual EType& type(int v, int u) { return E[v][u]->type; }//返回顶点v,u之间的边类型
virtual Te& edge(int v, int u) { return E[v][u]->data; } // 返回顶点v, u之间的边数据
virtual int& weight(int v, int u) { return E[v][u]->weight; }// 返回顶点v, u之间的边权重
//边的动态操作
virtual void insert(int v, int u, Te const& te, int w = 1);//在顶点v,u之间插入边
virtual Te remove(int v, int u);//在顶点v,u之间删除边
//图算法
template<typename T> void BFSearch(int v, T& t);//对每个顶点进行一次函数访问
template<typename T> void DFSearch(int v, T& t);//对每个顶点进行一次函数访问
void Prim();//Prim
void Kruskal(int v);//Kruskal
};
图的邻接矩阵表示类中的边集类:
数据结构:二维动态顺序表
template<typename Te>
struct Edge//边
{
Te data;//数据
int weight;//权重
EType type;//类型
Edge(Te const& d, int w) ://构造函数
data(d), weight(w), type(UNDETERMINED) {}
Edge(const Edge& e) :data(e.data), weight(e.weight), type(e.type) {}//复制构造函数
Edge& operator=(const Edge& e) {//重载赋值运算符
data = e.data; weight = e.weight; type = e.type; return *this;
}
};
图的邻接矩阵表示类中的点集类:
数据结构:动态顺序表
template<typename Tv>
struct Vertex//点
{
Tv data;//数据
int inDegree, outDegree;//入度,出度
VStatus status;//状态
int dTime, fTime;//时间标签
int parent;//父节点
int priority;//优先级数
Vertex() :data(), inDegree(0), outDegree(0), status(UNDISCOVERED),//构造函数
dTime(-1), fTime(-1), parent(-1), priority(INT_MAX) {}
Vertex(Tv const d) ://构造函数
data(d), inDegree(0), outDegree(0), status(UNDISCOVERED),
dTime(-1), fTime(-1), parent(-1), priority(INT_MAX) {}
};
最小生成树类:
数据结构:顺序表
template<typename Tv,typename Te>
class GraphMatrix :public Graph<Tv,Te>
{
private:
Vector<Vertex<Tv>>V;//顶点集
Vector<Vector<Edge<Te>*>> E;//边集
template<typename T> void DFSEARCH(int v, T& t, int*& visit);//
public:
GraphMatrix() { Graph<Tv,Te>::n() = 0; Graph<Tv, Te>::e() = 0; }
~GraphMatrix();
//顶点的基本操作
virtual Tv& vertex(int i) { return V[i].data; }//数据
virtual int inDegree(int i) { return V[i].inDegree; }
virtual int outDegree(int i) { return V[i].outDegree; }
virtual int firstNbr(int i) { return nextNbr(i, Graph<Tv, Te>::n()); }//返回首个邻接顶点
virtual int nextNbr(int i,int j);//返回下一个邻接顶点
virtual VStatus& status(int i) { return V[i].status; }//返回顶点的状态
virtual int& dTime(int i) { return V[i].dTime; }//返回顶点的时间标签dTime
virtual int& fTime(int i) { return V[i].fTime; }//返回顶点的时间标签fTime
virtual int& parent(int i) { return V[i].parent; }//返回顶点在遍历树中的父亲
virtual int& priority(int i) { return V[i].priority; }//返回顶点在遍历树中的优先级数
virtual int findVertex(const Tv & t);
//顶点的动态操作
virtual int insert(Tv const& vertex);//插入顶点,返回编号
virtual Tv remove(int i);//删除顶点,返回数据
//边的基本操作
virtual bool exists(int v, int u);//判断顶点v,u之间是否存在边
virtual EType& type(int v, int u) { return E[v][u]->type; }//返回顶点v,u之间的边类型
virtual Te& edge(int v, int u) { return E[v][u]->data; } // 返回顶点v, u之间的边数据
virtual int& weight(int v, int u) { return E[v][u]->weight; }// 返回顶点v, u之间的边权重
//边的动态操作
virtual void insert(int v, int u, Te const& te, int w = 1);//在顶点v,u之间插入边
virtual Te remove(int v, int u);//在顶点v,u之间删除边
//图算法
template<typename T> void BFSearch(int v, T& t);//对每个顶点进行一次函数访问
template<typename T> void DFSearch(int v, T& t);//对每个顶点进行一次函数访问
void Prim();//Prim
void Kruskal(int v);//Kruskal
};
3.业务功能层及其设计
(1)virtual Tv remove(int i);
实现功能:删除顶点,返回数据。
函数描述:先删除要删除结点的那列,再删除要删除结点的那行。
template<typename Tv, typename Te>
Tv GraphMatrix<Tv, Te>::remove(int i)
{
for (int j = 0; j < Graph<Tv, Te>::n(); j++)
if (exists(i, j))
{
delete E[i][j];
V[j].inDegree--;
}
E.remove(i);
Graph<Tv, Te>::n() -= 1;
Tv vBak = vertex(i);
V.remove(i);
for(int j = 0;j< Graph<Tv, Te>::n();j++)
if (Edge<Te>* e= E[j].remove(i))
{
delete e;
V[j].outDegree--;
}
return vBak;
}
- void Kruskal(int v);
实现功能:使用Kruskal算法得到最小生成数。
函数描述:先将图按照最小生成树的数据结构通过结构体数组的形式储存起来,再按照每条边的权值对每条边按从小到大排序,然后根据要添加的边对已添加的生成树进行遍历,若遍历到要插入的边的结点则跳过,若无则插入,最后得到最小生成树。
inline int Find(int* parent, int g) //通过parent[]找到可连接的边
{
while (parent[g] != 0)
{
g = parent[g];
}
return g;
}
template<typename Tv, typename Te>
inline int Finish(GraphMatrix<Tv, Te>& G, int* parent) //判断生成树是否完成,完成的标志是生成树的边等于顶点的数量减1
{
int n = 0;
for (int i = 0; i < G.n(); i++)
{
if (parent[i])
{
n++;
}
}
if (n == G.n() - 1)
{
return 1;
}
return 0;
}
template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::Kruskal(int v)
{
int* visit = new int[Graph<Tv, Te>::n()];
for (int j = 0; j < Graph<Tv, Te>::n(); j++)
visit[j] = 0;
int b = 0;
int size = Graph<Tv, Te>::e() / 2;
Vector<MinSpanTree<std::string, int>> p(size);
for (int i = 0; i < Graph<Tv, Te>::n(); i++)
for (int j = i + 1; j < Graph<Tv, Te>::n(); j++)
if (exists(i, j))
{
p[b].i = vertex(i);
p[b].j = vertex(j);
p[b].weight = edge(i, j);
b++;
}
p.sort();
for (int i = 0; i < Graph<Tv, Te>::e(); i++)
{
int a = Find(visit, findVertex(p[i].i));
int b = Find(visit, findVertex(p[i].j));
if (a != b) //如果a==b则表是a和b在同一颗生成树上,如果a和b连接则为生成环,不符合生成树
{
visit[a] = b;
std::cout << p[i].i << " " << p[i].j << " " << p[i].weight << std::endl;
}
if (Finish(*this, visit))
{
return;
}
}
}
3.结果测试
菜单:
查看图:
插入结点:
插入边:
删除结点:
删除边:
Prime算法得到最小生成树:
Kurskal算法得到最小生成树:
保存图信息: