2.6 File Transfer(树,c)

本文探讨了如何使用简化数据结构表示树状文件传输网络,并介绍了TSSN(Topological Sorting for Set Union)算法的实现,包括按秩归并、高度归并和规模归并,以及路径压缩优化的find操作。重点讲解了如何通过这些方法判断网络连接性和组件数量。
摘要由CSDN通过智能技术生成

原题直达:05-树8 File Transfer

集合的简化表示

请添加图片描述


请添加图片描述

题意理解

请添加图片描述


  • Sample Input 1:
5
C 3 2
I 3 2
C 1 5
I 4 5
I 2 4
C 3 5
S
  • Sample Output 1:
no
no
yes
There are 2 components.
  • Sample Input 2:
5
C 3 2
I 3 2
C 1 5
I 4 5
I 2 4
C 3 5
I 1 3
C 1 5
S
  • Sample Output 2:
no
no
yes
yes
The network is connected.

请添加图片描述


请添加图片描述

5代表计算机个数
C代表check表示查询3和2之间有没有联通,开始一条网线都没有,是不连通的,输出no
I 表示input输入,在3和2之间加一条网线…
S代表终止符
1自己是联通集(和自己联通),2,3,4,5是直接或间接联通的,所以输出系统里面有两个联通机请添加图片描述

请添加图片描述

最后输出这个网络是联通的

程序框架搭建

请添加图片描述

请添加图片描述
假设集合里的每个元素都是独立的,每一个都是自己的根节点,所以只要把S里面的每个元素都定义成-1就可以了

请添加图片描述

  • Input_connection 输入函数:读进两个计算机的编号,计算机是从1到n编号的,而集合S的下标是从0到n-1的,要做一个简单的映射,调用Find函数的时候,传进去的是u这台计算机的下标u-1,v也类似,即读进来两台计算机,分别查一下它们两个所属集合的根结点是什么,如果他们俩个还分属于不同集合的话,就在之间连一条网线,也就是把这两个集合合并在一起
  • Check_connection 查询函数:如果在check里面两个函数是一样的,就说明两个集合本身是联通的,输出yes,否则没联通就输出no
  • Check_network 检查网络是否联通,参数n表示有多少个根结点,即返回多少个联通集。扫描每一个s数组里的元素,只要他是小于0的不一,那么他是一个根节点,counter++;,最后数一下一共有多少个根节点,数完之后有两种情况,一种是只有一个根节点,表示整个集合全部联通了,否则就输出有多少个联通集

TSSN的实现

请添加图片描述

too simple sometimes naive(最原始简单版:很傻很天真)
请添加图片描述

按秩归并(对union的改进)

请添加图片描述

按高度归并

请添加图片描述

因为是负数,请添加图片描述
,root2的高度要大于root1的高度,树高+1的话,意味着负值-1

按规模归并

请添加图片描述

任何时候归并了两棵树以后,最后结果树的规模都会改变,变成原来两棵树的规模之和,所以当集合2比较大的时候,把集合2的根节点做一个改变,加上原来集合1的规模,反之集合1规模改变,加上集合2的规模,
按秩归并最坏情况的树高:O(logN)

路径压缩(对find的改进)

请添加图片描述

把路径上的每一个根节点都贴到了根节点的下面,该Find函数的递归调用实际上是一个伪递归,非常容易被转换成循环,不用担心系统堆栈爆掉。

  • 尾递归基于函数的尾调用, 每一级调用直接返回函数的返回值更新调用栈,而不用创建新的调用栈, 类似迭代的实现, 时间和空间上均优化了一般递归!

时间复杂度

请添加图片描述

请添加图片描述
当N充分大的时候,路径压缩比不做效率快的多,但是N只有10的4次方的时候,logN是一个很小的数字,很难比较两种方法优劣。

源码

#include<stdio.h>
#include<stdlib.h>

#define MaxSize 10001
typedef int ElementType;
typedef int SetName;
typedef ElementType SetType[MaxSize];
void Init(SetType s, int n) {
	/*默认集合元素全部初始化为-1*/
	/*for (; s[n]>=0; n=s[n])
		return n;
	*/
	for (int i = 0; i < n; i++)
		s[i] = -1;
	
}

/*路径压缩*/
SetName Find(SetType s, int x) {
	if (s[x] < 0)  // 本身已经是根 
		return x;
	else  // 先找到根,把根变成 x 的父结点,再返回根 
		return s[x] = Find(s, s[x]);
}

/*按秩(规模)归并*/
void Union(SetType s, SetName Root1, SetName Root2) {
	//S[root] = -元素个数
	if (s[Root1] < s[Root2]) {  //x1的元素多,少的并入多的中
		s[Root1] += s[Root2];
		s[Root2] = Root1;
	}
	else {
		s[Root2] += s[Root1];
		s[Root1] = Root2;
	}
}

/*输入函数*/
void Input_connection(SetType s) {
	ElementType u, v;
	scanf("%d %d", &u, &v);
	SetName root1 = Find(s, u - 1);  // 以数组下标存值,下标与存值差 1 
	SetName root2 = Find(s, v - 1);
	if (root1 != root2)
		Union(s, root1, root2);
}
/*查询函数*/
void check_connection(SetType s) {
	ElementType u, v;
	scanf("%d %d", &u, &v);
	SetName root1 = Find(s, u - 1);
	SetName root2 = Find(s, v - 1);
	if (root1 == root2)
		printf("yes\n");
	else
		printf("no\n");
}
/*检查网络是否联通,参数n表示有多少个根结点,即返回多少个联通集*/
void check_network(SetType s, int n) {
	int counter = 0;
	for (int i = 0; i < n; i++) {
		if (s[i] < 0)	counter++;
	}
	if (counter == 1)
		printf("The network is connected.");
	else
		printf("There are %d components.", counter);
}


int main() {
	SetType s;
	int n;
	char in;
	scanf("%d\n", &n);
	Init(s, n);
	do {
		//getchar();
		scanf("%c", &in);
		switch (in) {
		case 'I':Input_connection(s); break;
		case 'C':check_connection(s); break;
		case 'S':check_network(s, n); break;
		}
	} while (in != 'S');

	return 0;
}

运行
5
C 3 2
no
I 3 2
C 1 5
no
I 4 5
I 2 4
C 3 5
yes
S
There are 2 components.

请添加图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃鱼从来不吐刺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值