请使用简易层次聚类算法,为所给数据集构造一棵分层的树。 简易层次聚类算法思想: 简易层次聚类是一层一层地进行聚类的算法,可以对数据集从下而上地聚集,即 每次找到距离最短的两个数据样本,然后进行合并,直到全部合并完为止。整个 过程就是建立一个树结构,类似于下图。
执行步骤: 1)从数据集中找出两个距离最近的点(一个点就是一个数据样本),将这两个点 合并成一颗树,树的双亲结点的值为两个点的平均值。
2)重复第一步,直到只有一棵树为止。 请用简易层次聚类算法,为下面数据集构造一个分层的树。
数据集:![](https://i-blog.csdnimg.cn/direct/96254fec57d7485b803e87d2dc34b4c5.png)
点的距离公式为: 设两个点A、B以及坐标分别为,,则A和B两点之间的距离为:
要求:
(1) 请用文字描述该层次树的构造过程,如第一次哪两个样本组合成一棵 树,第二次哪两个样本组合在一起构成一颗树,依次类推,直到结束。画出这颗 层次树。
(2) 请编写代码实现上述层次树的构造(可以参考哈夫曼树算法的代码, 进行修改),并用先序遍历输出该树的结点编号(包括代码和运行结果的截图)。
(3) 请分析该算法和构造哈夫曼树算法的区别是什么?
(4) 请分析该算法的性能,如时间复杂度和空间复杂度等
(1) 请用文字描述该层次树的构造过程,如第一次哪两个样本组合成一棵 树,第二次哪两个样本组合在一起构成一颗树,依次类推,直到结束。画出这颗 层次树。
下标 | X | Y | parent | lchild | rchild |
---|---|---|---|---|---|
1 | 5.1 | 3.5 | 0 | 0 | 0 |
下标 | 距离值 | 编号 | 编号 |
---|---|---|---|
1 | 0.54 | 1 | 2 |
数据存储结构:
上方第一个:主要用来存储以上数据的各数据,利用函数对parent, lchild, rchild进行改变来实现树的构造。
第二个:用来存储用公式计算的距离值,并记录当前被计算的两个花株,以便在search()函数中找到距离最短的两个花株的编码并返回。
请编写代码实现上述层次树的构造
存储结构定义和主函数
/****存储结构为顺序表******/
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define MAX_SIZE 200
#define MAXInit 3367
typedef struct pt
{
double x;double y;
int parent;int lchild;int rchild;
}*PT;
typedef struct dis
{
double Distance;
int Data1;int Data2;
}*DS;
void Creat_PointTree(PT &O,int m);
void Init_PointTree(PT &O,int m);
DS Creat_DIS(PT O,int m);
void Renew_DIS(PT O,DS &D,int w);
void Search(PT O,DS D,int &s1,int &s2,int i);
void print_Tree(PT O,int m);
int main()
{
PT O;
int m;
scanf("%d",&m);
Creat_PointTree(O,m);//创建树
print_Tree(O,m);//输出树
return 0;
}
创建树的核心代码
void Creat_PointTree(PT &O,int m)//利用顺序表创建树
{
int n,i,j,s1,s2,x;
n = 2*m-1;
O = (PT)malloc((n+1)*sizeof(pt));
Init_PointTree(O,m);//赋值&&初始化
DS D;
D = Creat_DIS(O,m);//构造D[i]存储距离值
// for(i = 1;i <= (1+m-1)*(m-1)/2;i++)
// printf("%.2f\n",D[i].Distance);
for(i = m+1;i <= n;i++)
{
Search(O,D,s1,s2,i);
O[i].lchild = s1;//改变孩子编码
O[i].rchild = s2;
O[i].x = (O[s1].x+O[s2].x) / 2.0;
O[i].y = (O[s1].y+O[s2].y) / 2.0;
Renew_DIS(O,D,i);//更新D[i]的值
O[s1].parent = i;//改变双亲编码
O[s2].parent = i;
}
}
核心作用代码
在Creat_DIS()中:
两种初始化方法,也就是空间的分配
(1)第一种:开始奉陪一个较大的空间,将全部初始化为0或无穷,这会在之后的遍历操作中简单,但会浪费很多空间。在本文章中使用了这种。
(2)第二钟:根据等差数列的前n项和公式分配一个刚好那存储所有距离的空间,这里可以不用初始化,当需要更新D[i]时需要用realloc函数来对应增加空间,而且为了后续方便遍历,需要记录该表的长度。这可以大程度减少空间的浪费。
在Search()函数中:
(找到最小的距离&&O[D[i].Data1].parent ==0&&O[D[i].Data2].parent ==0)
再返回。
void Init_PointTree(PT &O,int m)
{
int i;
for(i = 1;i <= 2*m-1;i++)
{
O[i].parent = 0;
O[i].lchild = 0;
O[i].rchild = 0;
O[i].x = 0;O[i].y = 0;
}
//动态赋值
// n = 2*m-1;
// O = (OP)malloc(n+1*sizeof(op));
// for(i = 1;i <= m;i++)
// scanf("%f%f",&O[i].x,&O[i].y);
//静态赋值
O[1].x = 5.1; O[1].y = 3.5;
O[2].x = 4.9; O[2].y = 3.0;
O[3].x = 4.7; O[3].y = 3.2;
O[4].x = 6.1; O[4].y = 2.8;
O[5].x = 6.3; O[5].y = 2.5;
O[6].x = 6.1; O[6].y = 2.8;
O[7].x = 6.3; O[7].y = 2.5;
O[8].x = 6.5; O[8].y = 3.0;
O[9].x = 6.2; O[9].y = 3.4;
}
DS Creat_DIS(PT O,int m)//初始化D[i]的值和对已有的数据集的
{
DS D;
int i,j,x,w;
x = (m-1)*m/2+1; //可看成一个等差数列的和,以下有类似
D = (DS)malloc(MAX_SIZE*sizeof(dis));
for(w = 1;w < MAX_SIZE;w++)
D[w].Distance = 0;
w = 1;
for(i = 1; i <= m;i++)
{
for(j = i+1;j <= m;j++)
{
D[w].Distance = sqrt(pow(O[i].x-O[j].x,2)+pow(O[i].y-O[j].y,2));
D[w].Data1 = i;
D[w].Data2 = j;
w++;
}
}
return D;
}
void Search(PT O,DS D,int &s1,int &s2,int i)//返回距离最小两个花株的编码
{
int n,j,k;
double min = 3266;
n = 1;
while(n <= (1+i-2)*(i-2)/2)
{
if(D[n].Distance < min&&O[D[n].Data1].parent ==0&&O[D[n].Data2].parent ==0){
min = D[i].Distance;
k = n;
if(D[n].Distance == 0.0)
break;
}
n++;
}
s1 = D[k].Data1;
s2 = D[k].Data2;
}
void Renew_DIS(PT O,DS &D,int w)
{
int j,k;
j = (1+w-2)*(w-2)/2+1;
for(k = 1;k < w;k++)
{
D[j].Distance = sqrt(pow(O[w].x-O[k].x,2)+pow(O[w].y-O[k].y,2));
D[j].Data1 = w;
D[j].Data2 = k;
j++;
}
}
完整代码
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define MAX_SIZE 200
#define MAXInit 3367
typedef struct pt
{
double x;double y;
int parent;int lchild;int rchild;
}*PT;
typedef struct dis
{
double Distance;
int Data1;int Data2;
}*DS;
void Creat_PointTree(PT &O,int m);
void Init_PointTree(PT &O,int m);
DS Creat_DIS(PT O,int m);
void Renew_DIS(PT O,DS &D,int w);
void Search(PT O,DS D,int &s1,int &s2,int i);
void print_Tree(PT O,int m);
int main()
{
PT O;
int m;
scanf("%d",&m);
Creat_PointTree(O,m);
print_Tree(O,m);
return 0;
}
void Creat_PointTree(PT &O,int m)
{
int n,i,j,s1,s2,x;
n = 2*m-1;
O = (PT)malloc((n+1)*sizeof(pt));
Init_PointTree(O,m);//赋值&&初始化
DS D;
D = Creat_DIS(O,m);
// for(i = 1;i <= (1+m-1)*(m-1)/2;i++)
// printf("%.2f\n",D[i].Distance);
for(i = m+1;i <= n;i++){
Search(O,D,s1,s2,i);
O[i].lchild = s1;O[i].rchild = s2;//改变孩子编码
O[i].x = (O[s1].x+O[s2].x) / 2.0;
O[i].y = (O[s1].y+O[s2].y) / 2.0;
Renew_DIS(O,D,i);
O[s1].parent = i;O[s2].parent = i;//改变双亲编码
}
}
void Init_PointTree(PT &O,int m)
{
int i;
for(i = 1;i <= 2*m-1;i++){
O[i].parent = 0;O[i].lchild = 0;O[i].rchild = 0;O[i].x = 0;O[i].y = 0;
}
//动态赋值
// n = 2*m-1;
// O = (OP)malloc(n+1*sizeof(op));
// for(i = 1;i <= m;i++) scanf("%f%f",&O[i].x,&O[i].y);
//静态赋值
O[1].x = 5.1; O[1].y = 3.5;
O[2].x = 4.9; O[2].y = 3.0;
O[3].x = 4.7; O[3].y = 3.2;
O[4].x = 6.1; O[4].y = 2.8;
O[5].x = 6.3; O[5].y = 2.5;
O[6].x = 6.1; O[6].y = 2.8;
O[7].x = 6.3; O[7].y = 2.5;
O[8].x = 6.5; O[8].y = 3.0;
O[9].x = 6.2; O[9].y = 3.4;
}
DS Creat_DIS(PT O,int m)
{
DS D;
int i,j,x,w;
x = (m-1)*m/2+1;
D = (DS)malloc(MAX_SIZE*sizeof(dis));
for(w = 1;w < MAX_SIZE;w++)
D[w].Distance = 0;
w = 1;
for(i = 1; i <= m;i++){
for(j = i+1;j <= m;j++){
D[w].Distance = sqrt(pow(O[i].x-O[j].x,2)+pow(O[i].y-O[j].y,2));
D[w].Data1 = i;
D[w].Data2 = j;
w++;
}
}
return D;
}
void Search(PT O,DS D,int &s1,int &s2,int i)
{
int n,j,k;
double min = 3266;
n = 1;
while(n <= (1+i-2)*(i-2)/2){
if(D[n].Distance < min&&O[D[n].Data1].parent ==0&&O[D[n].Data2].parent ==0){
min = D[i].Distance;
k = n;
if(D[n].Distance == 0.0) break;
}
n++;
}
s1 = D[k].Data1;
s2 = D[k].Data2;
}
void Renew_DIS(PT O,DS &D,int w)
{
int j,k;
j = (1+w-2)*(w-2)/2+1;
for(k = 1;k < w;k++)
{
D[j].Distance = sqrt(pow(O[w].x-O[k].x,2)+pow(O[w].y-O[k].y,2));
D[j].Data1 = w;
D[j].Data2 = k;
j++;
}
}
void print_Tree(PT O,int m)
{
int i;
for(i = 1;i < 2*m;i++)
{
printf("%d %d %d %d %.2f %.2f\n",i,O[i].parent,O[i].lchild,O[i].rchild,O[i].x,O[i].y);
}
}
运行结果