05-树8 File Transfer (25分)(并查集的应用)(数据结构)(C语言实现)

We have a network of computers and a list of bi-directional connections. Each of these connections allows a file transfer from one computer to another. Is it possible to send a file from any computer on the network to any other?

Input Specification:
Each input file contains one test case. For each test case, the first line contains N (2≤N≤10
​4
​​ ), the total number of computers in a network. Each computer in the network is then represented by a positive integer between 1 and N. Then in the following lines, the input is given in the format:

I c1 c2  

where I stands for inputting a connection between c1 and c2; or

C c1 c2    

where C stands for checking if it is possible to transfer files between c1 and c2; or

S

where S stands for stopping this case.

Output Specification:
For each C case, print in one line the word “yes” or “no” if it is possible or impossible to transfer files between c1 and c2, respectively. At the end of each case, print in one line “The network is connected.” if there is a path between any pair of computers; or “There are k components.” where k is the number of connected components in this network.

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.
对于英语不好的人来说,
我们先来解释一下题目,题目要求输入 数字,表示有几个结点,然后输入一个大写英文和两个结点标号,如果大写英文是“C”则表示check 检查这两个结点是否能连的通,当大写字母是“I”则表示input a connection 输入一个连接,表示将这两个结点连接在一起。当输入“S”则表示Stop,停止程序,对于相应的检查我们需要输出yes或者no来表示这两个节点是够连接的上。

我们需要定义一个数组

#include <stdio.h>
 
#define MAXN 10000
 
int S[MAXN];

然后我们来看主函数的实现窗口

int main() {
 
    int n;
    scanf( "%d", &n );//输入有几个节点
    for( int i = 0; i < n; i++ ) S[i] = -1;//设置为-1,最后为-1的元素就是根
    char ch = getchar();//吸收缓冲区的字符
    while( 1 ) {
        ch = getchar();
        if( ch == 'S' )//遇到S则停止
            break;
        int a, b;
        scanf( "%d%d", &a, &b );//输入两个结点编号
        int parentA = Find( a );//找a元素的根
        int parentB = Find( b );//找b元素的根
        if( ch == 'C' ) {
            if( parentA != parentB ) {
                printf( "no\n" );//不属于同一个集合,无法建立连接
            }
            else printf( "yes\n" );
        }
        if( ch == 'I' )
            Union( parentA, parentB );
    }
    int count = 0;
    for( int i = 0; i < n; i++ ) {
        //printf( "%d ", S[i] );
        if( S[i] < 0 ) count++;//计算有几个集合,即几个连接区
    }
    //printf( "\n" );
    if( count == 1 ) printf( "The network is connected.\n" );
    else printf( "There are %d components.", count );
    return 0;
}

查找函数

int Find( int X )
{ /* 默认集合元素全部初始化为-1 */
    if ( S[X] < 0 ) /* 找到集合的根 */
        return X;
    else
        return S[X] = Find( S[X] ); /* 路径压缩 */
}

关于路径压缩,可能有些人没看明白,在路径压缩中,我们首先找到这个数的根节点,然后找到它的父结点,然后再找,直到找到根节点,根节点会返回到上一个节点并让上一个节点指向它,这原本就是指向它的。但是再往上返回并让那个节点也指向它呢?最后会形成从我们要查找的那个数到根节点那一条路径的元素,都会直接的指向根节点,这大大的压缩了路径
合并函数,我们来看一下图片,就很容易明白了。
开始是这样
在这里插入图片描述
后来变成这样了。
在这里插入图片描述

但是也许会有人问用和不用路径压缩有什么区别??
跑的效果差不多,那为什么还要用路径压缩呢?
在这里插入图片描述
在这里插入图片描述
那是因为本道题目给出的n的数量级很小,当n的数量级很大的时候,我们会发现两者的效率就差的很多了。所以我们在选择算法的时候一定要选择最简单的。

void Union( int Root1, int Root2 )//按秩归并
{ /* 这里默认Root1和Root2是不同集合的根结点 */
    /* 保证小集合并入大集合 */
    if ( S[Root2] < S[Root1] ) { /* 如果集合2比较大 ,因为在数组中,我们存入的树高不是正的,而是负数。*/
        S[Root2] += S[Root1];     /* 集合1并入集合2  */
        S[Root1] = Root2;
    }
    else {                         /* 如果集合2比较大 */
        S[Root1] += S[Root2];     /* 集合2并入集合1  */
        S[Root2] = Root1;
    }
}

总的代码如下

#include <stdio.h>
 
#define MAXN 10000
 
int S[MAXN];
 
void Union( int Root1, int Root2 )
{ /* 这里默认Root1和Root2是不同集合的根结点 */
    /* 保证小集合并入大集合 */
    if ( S[Root2] < S[Root1] ) { /* 如果集合2比较大 */
        S[Root2] += S[Root1];     /* 集合1并入集合2  */
        S[Root1] = Root2;
    }
    else {                         /* 如果集合1比较大 */
        S[Root1] += S[Root2];     /* 集合2并入集合1  */
        S[Root2] = Root1;
    }
}
 
int Find( int X )
{ /* 默认集合元素全部初始化为-1 */
    if ( S[X] < 0 ) /* 找到集合的根 */
        return X;
    else
        return S[X] = Find( S[X] ); /* 路径压缩 */
}
 
int main() {
 
    int n;
    scanf( "%d", &n );
    for( int i = 0; i < n; i++ ) S[i] = -1;
    char ch = getchar();
    while( 1 ) {
        ch = getchar();
        if( ch == 'S' )
            break;
        int a, b;
        scanf( "%d%d", &a, &b );
        int parentA = Find( a );
        int parentB = Find( b );
        if( ch == 'C' ) {
            if( parentA != parentB ) {
                printf( "no\n" );
            }
            else printf( "yes\n" );
        }
        if( ch == 'I' )
            Union( parentA, parentB );
    }
    int count = 0;
    for( int i = 0; i < n; i++ ) {
        //printf( "%d ", S[i] );
        if( S[i] < 0 ) count++;
    }
    //printf( "\n" );
    if( count == 1 ) printf( "The network is connected.\n" );
    else printf( "There are %d components.", count );
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值