数据结构实验3 :图的遍历生成树
实验内容及原理
- 键盘输入n个顶点和m条边(6<=n<=16,n-1<=m<=20)及相应权重,建立图的邻居矩阵和邻接表的存储形式,并输出该邻接矩阵和邻接表。
- 用Prim算法求其最小生成树。函数 void Prim(AMGraph G, VerTexType u) 以及输出边集数组的函数 void PrintEdge(Edgeset Sedge, int n)。
- 编写测试程序(即主函数),通过调用上述函数首先建立并输出无向带权图,然后生成最小生成树并输出(即输出边集)。
分析
实验内容比较简单,涉及到的知识主要有:图的存储,遍历,生成最小树算法,并且图的存储要用两种方法实现方式:邻接矩阵和邻接表。
prim 算法主要思路:维护两个集合:当前已经收录的结点集V{} , 和当前v中的结点直接相连的边的集合E{}。 每次从E{}中取出终点不在V{},并且长度最短的一条边,并更新V{},E{},直至全部结点都收录到E{}。从E{}中取出最短的一条边,简单实现可以用数组加遍历的方式,进阶方式是使用优先队列,可以减少时间复杂度和代码长度。
结构表示
//边结构
struct edge{
int from;
int to;
int len;
edge(int f, int t, int l){
from = f; to = t; len = l;
}
};
//用于定义优先队列对节点的排序方式
struct cmp{
bool operator()(edge &a, edge &b){
return a.len > b.len;
}
};
//结点结构
struct grap_node{
int val;
vector<edge>next;
};
邻接表实现
//无向图1,以邻接表为存储形式
class GRAP_one{
vector<grap_node> data;
public:
//展示邻接表
void display(){
cout << "================= display ===============\n";
for (int i = 0; i < data.size(); i++){
cout << i+1; //还原为输入时的数值
for (int y = 0; y < data[i].next.size(); y++){
printf(" -> %d", data[i].next[y].to+1);
}
cout << endl;
}
cout << "==========================================\n";
}
//初始化邻接表
void init(){
cout << "Please input the node number and edge number : >";
int n, e;
cin >> n >> e;
data.resize(n);
cout << "Please input all edge with format: from to length : \n";
for (int i = 0; i < e; i++){
int f, t, l;
scanf("%d%d%d", &f, &t, &l);
f--, t--; //输入结点以1为起点,保存时以0为起点
data[f].next.push_back({f, t, l });
data[t].next.push_back({t, f, l });
}
cout << "input over.... \n";
}
//生成并展示最小生成树
void prim(){
priority_queue<edge,vector<edge>,cmp>epq;
set<int> nset;
set<int>::iterator it;
nset.insert(0);
for (int i = 0; i < data[0].next.size(); i++){
epq.push({ 0, data[0].next[i].to, data[0].next[i].len });
}
while (epq.empty() == false){
edge temp = epq.top();
epq.pop();
it = nset.find(temp.to);
if (it != nset.end()){
continue;
}
printf("%d ---> ( %d ) ---> %d \n", temp.from+1, temp.len, temp.to+1);
nset.insert(temp.to);
for (int i = 0; i < data[temp.to].next.size(); i++){
epq.push({ temp.to, data[temp.to].next[i].to, data[temp.to].next[i].len });
}
}
}
};
邻接矩阵实现
//无向图2,以矩阵为存储形式
class GRAP_two{
private:
int **data; //邻接矩阵
int nn, en; //node number ,edge number
public:
//初始化图
void init(){
cout << "Please input node number, edge number: >";
scanf("%d %d", &nn, &en);
data = (int**)malloc(sizeof(int*)*nn); //申请内存
for (int i = 0; i < nn; i++){
data[i] = (int*)malloc(sizeof(int)*nn);
memset(data[i], 0, sizeof(int)*nn);
}
cout << "Please input data with format : from to lenght \n";
for (int i = 0; i < en; i++){
int f, t, l;
scanf("%d%d%d", &f, &t, &l);
data[f-1][t-1] = data[t-1][f-1] = l;
}
cout << "input data over... \n\n";
}
//展示邻接矩阵
void display(){
cout << "====================== Display of grap ============" << endl;
for (int i = 0; i < nn; i++){
for (int y = 0; y < nn; y++){
printf(" %d ", data[i][y]+1);
}
cout << endl;
}
cout << "===================================================\n\n";
}
void prim(){
priority_queue<edge, vector<edge>, cmp>epq;
set<int> nset;
set<int>::iterator it;
nset.insert(0);
for (int i = 0; i < nn; i++){
if (data[0][i] == 0 ) continue;
epq.push({ 0, i, data[0][i]});
}
while (epq.empty() == false){
edge temp = epq.top();
epq.pop();
it = nset.find(temp.to);
if (it != nset.end()){
continue;
}
printf("%d --->( %d ) ---> %d \n", temp.from + 1, temp.len, temp.to +1);
nset.insert(temp.to);
for (int i = 0; i < nn; i++){
if (data[temp.to][i] == 0) continue;
epq.push({ temp.to, i, data[temp.to][i] });
}
}
}
};
测试代码
//测试 GRAP_one 功能
void test_GRAP_one(){
GRAP_one grap;
grap.init();
grap.display();
grap.prim();
}
//测试 GRAP_two 功能
void test_GRAP_two(){
GRAP_two grap;
grap.init();
grap.display();
grap.prim();
}
int main(){
test_GRAP_one();
//test_GRAP_two();
return 0;
}
##### 运行结果
模拟数据
6 8
1 2 8
1 3 7
3 4 2
3 5 20
2 5 3
2 3 4
4 6 10
5 6 100