首页 > 试题广场 >

并查集的实现

[编程题]并查集的实现
  • 热度指数:4518 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定一个没有重复值的整形数组arr,初始时认为arr中每一个数各自都是一个单独的集合。请设计一种叫UnionFind的结构,并提供以下两个操作。
  1. boolean isSameSet(int a, int b): 查询a和b这两个数是否属于一个集合
  2. void union(int a, int b): 把a所在的集合与b所在的集合合并在一起,原本两个集合各自的元素以后都算作同一个集合
[要求]
如果调用isSameSet和union的总次数逼近或超过O(N),请做到单次调用isSameSet或union方法的平均时间复杂度为O(1)

输入描述:
第一行两个整数N, M。分别表示数组大小、操作次数
接下来M行,每行有一个整数opt
若opt = 1,后面有两个数x, y,表示查询(x, y)这两个数是否属于同一个集合
若opt = 2,后面有两个数x, y,表示把x, y所在的集合合并在一起


输出描述:
对于每个opt = 1的操作,若为真则输出"Yes",否则输出"No"
示例1

输入

4 5
1 1 2
2 2 3
2 1 3
1 1 1
1 2 3

输出

No
Yes
Yes

说明

每次2操作后的集合为
({1}, {2}, {3}, {4})
({1}, {2, 3}, {4})
({1, 2, 3}, {4})

备注:

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

#define SWAP(a, b) { typeof(a) t = a; a = b; b = t; }

typedef enum { OK = 1, ERROR = -1 } Status;

const int N = 1E6;
int parents[N], ranks[N];

Status InitUnionFind(const int n) {
  int i;
  for (i = 1 ; i <= n; ++i) {
    *(parents + i) = i;
    *(ranks + i) = 1;
  }
  return OK;
}

// Path Compression
int Find(const int x) {
  if (*(parents + x) ^ x)
    *(parents + x) = Find(*(parents + x));
  return *(parents + x);
}

// Union by rank
void Union(int a, int b) {
  a = Find(a), b = Find(b);
  if (a == b) return;
  // a 始终指向秩小的树/集合/连通分量/簇的根或ID
  if (*(ranks + a) > *(ranks + b)) SWAP(a, b);
  
  *(parents + a) = b;
  if (*(ranks + a) == *(ranks + b))
    ++*(ranks + b);
}

int IsConnected(int a, int b) {
  return Find(a) == Find(b);
}

int main(const int argc, const char* const argv[]) {
  int n, m;
  fscanf(stdin, "%d %d", &n, &m);
  InitUnionFind(n);
  
  int op, a, b, i;
  while (m--) {
    fscanf(stdin, "%d %d %d", &op, &a, &b);
    if (op == 1) puts(IsConnected(a, b) ? "Yes" : "No");
    else         Union(a, b);
  }
  return 0;
}

发表于 2021-08-09 13:22:47 回复(0)

问题信息

上传者:小小
难度:
1条回答 5804浏览

热门推荐

通过挑战的用户

查看代码