寻找具有树特征无向图的最小高度树---C语言

== 一、问题来源==

对于一个具有树特征的无向图,我们可选择任何一个节点作为根。图因此可以成为树,在所有可能的树中,具有最小高度的树被称为最小高度树。给出这样的一个图,写出一个函数找到所有的最小高度树并返回他们的根节点。该图包含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);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用Prim算法求带权无向图最小生成C语言实现: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define MAXV 1000 // 最大顶点数 typedef struct { int adj[MAXV][MAXV]; // 邻接矩阵 int n; // 顶点数 } Graph; int prim(Graph* g) { int vis[MAXV] = {0}; // 标记顶点是否已被加入生成 int dist[MAXV]; // 存储每个顶点到生成的最短距离 int parent[MAXV]; // 存储每个顶点在生成中的父节点 int i, j, u, min_dist, min_index, sum = 0; // 初始化dist数组和parent数组 for (i = 0; i < g->n; ++i) { dist[i] = INT_MAX; parent[i] = -1; } // 从0号顶点开始构建生成 dist[0] = 0; u = 0; // 执行n-1次循环,每次将一个顶点加入生成 for (i = 0; i < g->n-1; ++i) { vis[u] = 1; // 将u加入生成 min_dist = INT_MAX; min_index = -1; // 更新dist数组和parent数组 for (j = 0; j < g->n; ++j) { if (!vis[j] && g->adj[u][j] < dist[j]) { dist[j] = g->adj[u][j]; parent[j] = u; } if (!vis[j] && dist[j] < min_dist) { min_dist = dist[j]; min_index = j; } } // 更新sum sum += min_dist; // 将与min_index相连的顶点的dist值更新为新的最短距离 u = min_index; } return sum; } int main() { Graph g = { { {0, 7, 0, 5, 0, 0}, {7, 0, 8, 9, 7, 0}, {0, 8, 0, 0, 5, 0}, {5, 9, 0, 0, 15, 6}, {0, 7, 5, 15, 0, 8}, {0, 0, 0, 6, 8, 0} }, 6 // 顶点数 }; printf("Minimum weight of the MST: %d\n", prim(&g)); return 0; } ``` 在本例中,我们使用邻接矩阵存储图,并通过结构体`Graph`来表示。在`prim`函数中,我们使用一个`vis`数组来标记哪些顶点已被加入生成,使用`dist`数组来存储每个顶点到生成的最短距离,使用`parent`数组来存储每个顶点在生成中的父节点。在循环中,我们首先将0号顶点加入生成,然后执行n-1次循环,每次将一个顶点加入生成。在每次循环中,我们更新`dist`数组和`parent`数组,并找到未被加入生成的顶点中距离生成最近的顶点,将其加入生成。最后,我们将所有边的权重相加即得到最小生成的权重。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值