<<最小生成树>> 主文件smallestTree.cpp
/***
文件名:SmallestTree.cpp
最小生成树主算法文件
by [email]lzbgt@126.com[/email]
LastModefied:2004.12.11
****/
#include<iostream>
#include<stdlib.h>//产生随机数组用
#include<time.h> //同上
#include"base" //所用到的自定义数据结构定义和实现文件
using namespace std;
bool IsCycle(Graph& graph,MyArc& arc); //判断是否构成回路
void kruskal(const Graph& graph,Graph& smtree);//克鲁斯卡尔算法
void SmallestTreeOutput(const Graph& smtree); //输出最小生成树
void SetMatrix(int vexnum,int *matrix); //用随机数组初始化matrix数组并且打印
/*
主函数
*/
void main()
{
char i;
cout<<"请输入顶点数目:";
cin>>i;
int vex=i-'0';
int *matrix=new int[vex*vex];
cout<<endl;
SetMatrix(vex,matrix);
Graph graph(vex,matrix),smtree(vex);
kruskal(graph,smtree);
SmallestTreeOutput(smtree);
delete []matrix;
}
//用随机数组初始化matrix数组并且打印
void SetMatrix(int vexnum,int *pmatrix)
{
srand((unsigned)time(NULL));
for(int i=0;i<vexnum;++i)//产生随机权值矩阵
{
for(int j=i;j<vexnum;++j)
{
if(j==i)
{
pmatrix[i*vexnum+j]=0;
continue;
}
int rnum=rand();rnum%=99;rnum++;//产生1~99的随机整数作为边的权值
pmatrix[i*vexnum+j]=rnum;
pmatrix[j*vexnum+i]=rnum;
}
}
cout<<"***随机产生的各边权值矩阵 [顶点数为 "<<vexnum<<"] ****/n";
for(int i=0;i<vexnum;++i)//输出随机权值矩阵
{
for(int j=0;j<vexnum;++j)
{
cout<<pmatrix[i*vexnum+j]<<"/t";
}
cout<<endl;
}
}
//判断连通边arc后 图graph 是否存在回路
bool IsCycle(Graph& graph, MyArc& arc)
{
list<int> mylist;
mylist.push_back(arc.m_beginVex);
int *ps=new int[graph.m_vexnum];
for(int i=0;i<graph.m_vexnum;++i)
ps[i]=0;
while(!mylist.empty())
{
int x=mylist.front();
ps[x]=1;
mylist.pop_front();
for(int i=0;i<graph.m_vexnum;++i)
{
if(graph.m_pmatrix[i+x*graph.m_vexnum]!=0)
{
if(i==arc.m_endVex) return true;
if(ps[i]!=1) mylist.push_back(i);
}
}
}
delete[] ps;
return false;
}
//克鲁斯卡尔算法
void kruskal(const Graph& graph,Graph& smtree)
{
MyQueues arcqueues;//保存从小到大排列的边
arcqueues.InsertGraph(graph);
MyArc myarc;//Arc表示边的类型
int arcnum=0; //边的个数
while(arcnum<graph.m_vexnum-1)
{
myarc=arcqueues.pop();
if(!IsCycle(smtree,myarc))
{
smtree.insert(myarc);
++arcnum;
}
}
}
//输出最小生成树
void SmallestTreeOutput(const Graph& smtree)
{
cout<<"最小生成树:"<<endl;
for(int i=0;i<smtree.m_vexnum;++i)//输出最小树
for(int j=i+1;j<smtree.m_vexnum;++j)
if(smtree.m_pmatrix[i*smtree.m_vexnum+j])
cout<<'('<<i<<','<<j<<','<<smtree.m_pmatrix[i*smtree.m_vexnum+j]<<')'<<endl;
}
[ 此贴被lzbgt在2005-06-08 20:56重新编辑 ]
[20 楼] From:Unkown | Posted:2005-06-08 20:40|
lzbgt
寂寞地飞
级别: 正版主
精华: 1
发帖: 104
威望: 356 点
金钱: 892 RMB
贡献值: 4 点
注册时间:2004-12-02
最后登陆:2005-11-17
--------------------------------------------------------------------------------
<<最小生成树>>报告文档
5.6“最小生成树问题”设计报告
题目:2.2 编制一个求出N个顶点图的最小生成树程序
班级: 姓名:*** 学号: 完成日期: 2004-12-11 签字:
一 需求分析
(1)在n个城市间建设通信网络,只需要架设n-1条线路即可。以最低的代价建设这个通信网,即求图的最小生成树。
(2)利用克鲁斯卡尔算法求网的最小生成树。
(3)利用自定义的队列结构存放连通分量。
(4)以文本形式输出最小生成树中的各条边及它们的权值。输出格式为(int a,int b,int n),其中a,b为顶点序号,n为ab边的权;
(5)程序运行流程:
1)提示输入顶点数目;
2)接受输入,按照项目要求产生边权值的随机矩阵;然后求解最小生成树;
3)输出最小生成树并且退出;
(6)测试数据
省略,见测试部分
二 概要设计
1.表示边的类定义和接口:
class MyArc
{
public:
int m_beginVex;
int m_endVex;
int m_weight;
MyArc(int beginVex,int endVex,int weight);
MyArc(){}
//重载运算符
inline bool operator < (const MyArc& arc){ return m_weight<arc.m_weight;}
inline bool operator == (const MyArc& arc){ return m_weight==arc.m_weight;}
inline bool operator > (const MyArc& arc){ return m_weight>arc.m_weight; }
};
2. 用邻接矩阵表示的图类的定义和接口:
class Graph
{
private:
int m_vexnum;
int m_arcnum;
int *m_pmatrix;
public:
~Graph();
Graph(int vexnum);
Graph(int vexnum,int *pmatrix);
void insert(MyArc arc);//连通arc 边
bool bound(int x); //判断顶点x是否已与其它顶点连通
};
3. 自定义队列,用于存放连通图,或按权排列后的边:
class MyQueues
{
public:
list<MyArc> m_list;
MyQueues(){}
void insert(const MyArc& arc); //按权值大小排序插入
void InsertGraph(const Graph &graph); //将图的连通分量插入队列
MyArc pop();//出队列
};
3.本程序的结构
1)主程序模块:
void main()
{
申明边权值矩阵数组并用随机函数初始化;
创建图;
调用克鲁斯卡尔算法函数;
输出边的权值矩阵,最小生成树中的各条边及它们的权值
退出;
}
2)带权的边类模块---实现带权边的存储和运算。
邻接矩阵类模块---实现图的状态记录和相关操作。
自定义队列类模块---实现边的按权存贮和相关操作。
3)核心kruskal算法模块---用克鲁斯卡尔算法求出最小生成树
各模块调用关系:
三 详细设计
1) 带权边的类MyArc:
class MyArc
{
public:
int m_beginVex;
int m_endVex;
int m_weight;
MyArc(int beginVex,int endVex,int weight);
MyArc(){}
bool operator < (const MyArc& arc)
{
return m_weight<arc.m_weight;
}
bool operator == (const MyArc& arc)
{
return m_weight==arc.m_weight;
}
bool operator > (const MyArc& arc)
{
return m_weight>arc.m_weight;
}
};
MyArc::MyArc(int beginVex,int endVex,int weight):m_beginVex(beginVex),m_endVex(endVex),m_weight(weight)
{}
2) 表示图的邻接矩阵类Graph:
class Graph
{
public:
int m_vexnum;
int m_arcnum;
int *m_pmatrix;
public:
~Graph();
Graph(int vexnum);
Graph(int vexnum,int *pmatrix);
void insert(MyArc arc);//按权值大小排序插入
bool bound(int x); //判断顶点x是否已与其它顶点连通
};
//构造函数
Graph::Graph(int vexnum)
{
m_pmatrix=new int[vexnum*vexnum];
m_vexnum=vexnum;m_arcnum=0;
for(int i=0;i<vexnum*vexnum;++i) m_pmatrix[i]=0;
}
//构造函数
Graph::Graph(int vexnum,int *pmatrix)
{
m_vexnum=vexnum;
// m_arcnum=arcnum;
m_pmatrix=new int[m_vexnum*m_vexnum];
for(int i=0;i<m_vexnum*m_vexnum;++i)
m_pmatrix[i]=pmatrix[i];
}
//测试 顶点x是否已与其他点连通
bool Graph::bound(int x)
{
for(int i=0;i<m_vexnum;++i) if(m_pmatrix[x+i*m_vexnum]!=0) return true;
return false;
}
//在邻接表中连通 arc表示的边,并且设置权
void Graph::insert(MyArc arc)
{
m_pmatrix[arc.m_beginVex*m_vexnum+arc.m_endVex]=arc.m_weight;
m_pmatrix[arc.m_endVex*m_vexnum+arc.m_beginVex]=arc.m_weight;
++m_arcnum;
}
//析构
Graph::~Graph()
{
delete[] m_pmatrix;
}
3) 按权存储边的有序队列类MyQueues:
class MyQueues
{
public:
list<MyArc> m_list;
MyQueues(){}
void insert(const MyArc& arc);//边按权值插入队列中合适位置,
void InsertGraph(const Graph &graph);//将图的连通分量插入队列
MyArc pop();
};
//边出队
MyArc MyQueues::pop()
{
MyArc arc=m_list.front();
m_list.pop_front();
return arc;
}
//边按权值插入队列中合适位置,
void MyQueues::insert(const MyArc& arc)
{
list<MyArc>::iterator pos=m_list.begin();
while(pos!=m_list.end())
{
if(*pos>arc) break;
else ++pos;
}
m_list.insert(pos,arc);
}
//将图的连通分量插入队列
void MyQueues::InsertGraph(const Graph &graph)
{
for(int i=0;i<graph.m_vexnum;++i)
{
for(int j=i+1;j<graph.m_vexnum;++j)
if(graph.m_pmatrix[i*graph.m_vexnum+j]) insert(MyArc(i,j,graph.m_pmatrix[i*graph.m_vexnum+j]));
}
}
4) kruskal算法:
void kruskal(const Graph& graph,Graph& smtree)
{
MyQueues arcqueues;//保存从小到大排列的边
arcqueues.InsertGraph(graph);
MyArc myarc;//Arc表示边的类型
int arcnum=0; //边的个数
while(arcnum<graph.m_vexnum-1)
{
myarc=arcqueues.pop();
if(!IsCycle(smtree,myarc))
{
smtree.insert(myarc);
++arcnum;
}
}
}
5)判断是否有回路的IsCycle函数:
bool IsCycle(Graph& graph, MyArc& arc)
{
list<int> mylist;
mylist.push_back(arc.m_beginVex);
int *ps=new int[graph.m_vexnum];
for(int i=0;i<graph.m_vexnum;++i)
ps[i]=0;
while(!mylist.empty())
{
int x=mylist.front();
ps[x]=1;
mylist.pop_front();
for(int i=0;i<graph.m_vexnum;++i)
{
if(graph.m_pmatrix[i+x*graph.m_vexnum]!=0)
{
if(i==arc.m_endVex) return true;
if(ps[i]!=1) mylist.push_back(i);
}
}
}
delete[] ps;
return false;
}
4.main和其他函数核心代码
参见 SmallestTree.cpp文件和MyDS.h文件
四 调试分析和心得体会
1.在调试MyQueues类的时候,由于对数组的按行存储理解的不够好导致开始时未能获得正确的数据,
经单步跟踪调试后发现下标超出范围,修正后即可正常运行。
2.在本实验中,自定义了3个类,定义了基于它们的数据和操作,它们是本程序的实现基础。
3.由于翻译核心算法 kruskal的时间复杂度与边的数目的函数有线性关系,同时IsCycle函数含有while和for循环,在顶点数目不多的时候,算法时间很快,适用于稀疏矩阵。
4.体会:
通过该实验,本人对求最小生成树的kruskal有了更深刻的理解,对用数组存放边的权值和基于数组的指针操作有了更深的认识,在刚开始调试的时候总输出乱码,经过查阅<<Data Structures with C++ Using STL>>一书,才了解到字符数组与C 风格的 字符串是有区别的,后者是以NULL结尾的,前者必须将最后一个单元赋值NULL即0才能更正确地用C的字符函数.
在设计3个相关类的时候,为了使用STL中的list模板类,查阅了<<Data Structures with C++ Using STL>>使我对STL的认识提高到新的层次。
本实习没有用到base文件中定义的类。
五 测试结果:
1.
INPUT:
输入的顶点数目:4
OUTPUT:
输出边的权值随机矩阵:
0 39 29 8
39 0 23 1
29 23 0 8
8 1 8 0
输出的最小生成树:
(0,3,8)
(1,3,1)
(2,3,8)
2.
INPUT:
输入的顶点数目:9
OUTPUT:
输出边的权值随机矩阵:
0 3 43 41 87 99 35 81 39
3 0 56 31 89 50 64 56 43
43 56 0 71 56 17 65 45 21
41 31 71 0 34 76 32 71 38
87 89 56 34 0 32 79 70 73
99 50 17 76 32 0 43 49 94
35 64 65 32 79 43 0 97 31
81 56 45 71 70 49 97 0 48
39 43 21 38 73 94 31 48 0
输出的最小生成树:
(0,1,3)
(1,3,31)
(2,5,17)
(2,7,45)
(2,8,21)
(3,6,32)
(4,5,32)
(6,8,31)
3.其他,略.
六 附录
文件base含有自定义的MyArc类,Graph类,MyQueues类。
文件 SmallestTree.cpp 为本设计项目的主测试文件,含有main,kruskal算法函数和其他相关函数.