== 一、问题来源==
对于一个具有树特征的无向图,我们可选择任何一个节点作为根。图因此可以成为树,在所有可能的树中,具有最小高度的树被称为最小高度树。给出这样的一个图,写出一个函数找到所有的最小高度树并返回他们的根节点。该图包含n个节点,标记为0到n-1。给定数字n和一个无向边edges列表(每一个边都是一对标签)你可以假设没有重复的边会出现在edges 中。由于所有的边都是无向边,[0,1] 和[1,0]是相同的,因此不会同时出现在edges 里。
示例1:
输入: n=4, edges =[[1, 0],[1, 2],[1, 3]]
0
|
1
/ \
2 3
输出:【1】
示例2:
输入: n=6, edges = [[O, 3],[1, 3],[2, 3], [4,3],[5, 4]]
0 1 2
\ | /
3
|
4
|
5
输出: [3, 4]
说明:
根据[树的定义],树是一个无向图,其中任何两个顶点只通过一条路径连接。换句话说,一个任何没有简单环路的连通图都是一棵树。
树的高度是指根节点和叶子节点之间最长向下路径.上边的数量。
二、心路历程
使用C语言来解决摘要中所述的在无向图中寻找最小高度树的问题,对于一个没有学过数据结构的小白来讲,为了弄明白解题的整个过程差点挠光了头发。起初在网络上寻找相关的解题方法,简单的一搜,哦豁,是一道经典的leedcode题目,可参考的博客多如牛毛。此时心里那是美滋滋的,又可以白嫖一下前辈们的智慧结晶啦。可是事情如果如此简单,就不会诞生今天这一篇分享啦。当我满怀期待的打开一篇篇博客的时候,看到C++/java实现,我那脆弱的小心脏就逐渐变得哇凉哇凉的。C++没学过啊,Java我也不会啊。那你还会啥?我就会个C,还是没啥编程经验的那种。这可咋办呀,看了他们的解题思路。嗯,无向图,广度优先搜索?拓扑结构?越看越懵,这时候心里慌的一批。既然看不懂,就不掩耳盗铃了。求人不如求己,自己先分析问题,恶补一下数据结构的知识,然后跟几位有经验的同事讨论了一下,整理出了自己的解题思路。
三、解题思路
建立无向图 -> 遍历无向图 -> 对称性 -> 依次去除叶节点
无向图是由定点和边两部份组成。表示图的方法有很多种,经过对比,我选择了使用邻接矩阵来表示图。因为相比于链表更容易让人理解,且运用无向图在邻接矩阵中的对称特性,可以很直观的判定无向图中每一个顶点的度。
邻接矩阵
如上图所示:图的邻接矩阵存储方式是用两个数组来表示图。一个意为数组存储图中的顶点信息,一个二维数组(称为邻接矩阵)存储图中边的信息。
有了这个矩阵我们就可以直观的看出图中的信息:
1、顶点之间有联系,将他们的所在边的度置为 1;
2、某个顶点的度,其实就是这个定点v(i)在邻接矩阵中第i行的(或者第i列)的元素之和,例如v(1)的度就是1+0+1+0=2;
3、顶点v(i)的所有邻接点就是矩阵中第i行arc(i)(j)为 1 的点;
4、无向图中顶点指向是相互的,所以arc(i)(j)的值呈主对角线对称分布;
每次遍历邻接矩阵,得出度为1的顶点,此顶点在树结构中一定是叶节点。根据无向图在邻接矩阵中的对称原理,将该点与临界点对应的连接边的度均置为0,即完成了剪树叶的操作。执行递归操作,直至剩余1~2个顶点,它们就是我们寻找的最小高度树的根节点。
四、示例2测试
五、代码示例(完整)
/*******************************************************************************
* Filename:
* FMHT
*Description:
* Find the Minimum Height Tree
*Author:
QH
*Time:
2020.8.24
********************************************************************************/
#include <stdio.h>
typedef int VertexType;
typedef int EdgeType;
#define MAXVEX 100
#define ZERO 0
typedef struct{
VertexType vexs[MAXVEX];
EdgeType arc[MAXVEX][MAXVEX];
int numVertexes, numEdges;
} MGraph;
void CreateMGraph(MGraph *G);
void RemoveLeave(MGraph *G);
int main(){
//Building undirected graph with adjacency matrix
MGraph G;
CreateMGraph(&G);
printf("Output the root node of the minimum height tree\n");
RemoveLeave(&G);
return 0;
}
/*******************************************************************************
*****************
* FUNCTION
* CreateMGraph
*DESCRIPTION
* Representstion of undirected graph by adjacency matrix.
*PARAMETERS
* G [MGraph *] structure
*RETURNS
* void
*****************
*******************************************************************************/
void CreateMGraph(MGraph *G){
int i, j, k;
printf("Please enter the number of vertexes and edges:");
scanf("%d,%d", &G -> numVertexes, &G -> numEdges);
printf("Please enter vertex:\n");
for (i = 0; i < G -> numVertexes; i ++)
scanf("%d", &G -> vexs[i]);
for (i = 0; i < G -> numVertexes; i ++)
for (j = 0; j < G -> numVertexes; j ++)
G -> arc[i][j] = ZERO;
for (k = 0; k < G -> numEdges; k ++){
printf("please enter (vi,vj):");
scanf("%d, %d", &i, &j);
G -> arc[i][j] = 1;
G -> arc[j][i] = G -> arc[i][j];
}
//Adjacency matrix of output undirected graph
printf("Adjacency matrix of output undirected graph:\n");
for (i = 0; i < G -> numVertexes; i ++){
for (j = 0; j < G -> numVertexes; j ++){
printf("%d\t", G -> arc[i][j]);
}
printf("\n");
}
}
/*******************************************************************************
*****************
* FUNCTION
* RemoveLeave
*DESCRIPTION
* Use recursive loops to remove leaf nodes layer by layer.
*PARAMETERS
* G [MGraph *] structure
*RETURNS
* void
*****************
*******************************************************************************/
void RemoveLeave(MGraph *G){
int i, j; //for circulation
int sum; //Sum of connected edges of each vertex
int n = 0; //Number of remaining vertices
int m[G -> numVertexes]; //save
//initialize m[]
for (i = 0; i < G -> numVertexes; i ++){
m[i] = ZERO;
}
//Determine whether the vertex is a leaf. yes set 1,not set 2
for (i = 0; i < G -> numVertexes; i ++){
sum = 0;
for (j = 0; j < G -> numVertexes; j ++){
sum = sum + G -> arc[i][j];
if (sum > 1){
n = n + 1;
m[i] = 2;
break;
}
if (sum == 1){
m[i] = 1; //leaf
}
}
}
//remove leaf
for (i = 0; i < G -> numVertexes; i ++){
if (m[i] == 1){
for (j = 0; j < G -> numVertexes; j ++){
G -> arc[i][j] = 0;
G -> arc[j][i] = 0;
}
}
if (m[i] == 2){
if (n < 3){
//Output the root node of the minimum height tree
printf("%d\t", G -> vexs[i]);
}
}
}
//Recursion and termination condition judgment
if (n < 3){
return ;
}
else{
RemoveLeave(G);
}
}