并查集思想及例题

并查集是一种高效处理不相交集合合并与查询的数据结构,常用于判断连通性、Kruskal算法和最近公共祖先等问题。本文通过剪邮票问题举例,介绍了并查集的使用,包括初始化、合并和查询操作,并通过实际代码展示了并查集解决连通性判断的过程,以及如何通过路径压缩优化提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

并查集是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并和查询问题。经典的应用有:判断连通性、最小生成树 Kruskal 算法、最近公共祖先(Least Common Ancestors, LCA)等。并查集在算法竞赛中也十分常见:一是简单且高效,二是应用很直观,三是容易和其他数据结构和算法结合。


例题:

剪邮票 - 蓝桥云课 (lanqiao.cn)

有一个简单粗暴的方法:暴力求排列 + 检查连通性:

  1. 用递归暴力列出所有可能的排列:从 12 个数中选 5 个数。
  2. 判断这 5 个数是否连通.
#include<bits/stdc++.h>
using namespace std;
int star[]={1,2,3,4,6,7,8,9,11,12,13,14};
int num=0; //统计排列的个数
void Perm(int begin,int end){
    if(begin == 5)  //得到一个5个数的排列
          num++;    //统计排列个数
    else
       for(int i = begin;i <= end;i++) {
            swap(star[begin],star[i]); 
            Perm(begin+1,end);
            swap(star[begin],star[i]); 
        }
}
int main(){
    Perm(0,11);  //求从第0个数到第11个数的全排列。
    cout << "total arrange=" << num/120 <<"\n";
       //注意5个数的排列不需要有序,所以除以120,这120个都是重复的。
       //例如: 1 2 3 4 5和2 3 4 1 5这种数是重复的,除去5!
    return 0;
}

检查连通性(一个排列的 5 个数,检查每个数是否与其它的数相连)

可以先考虑 2 个数的连通。有一个好用的小技巧:在原图中向上为 -4 ,向下为+4 ,向左为 -1,向右为 +1,但是遇到 “3,4,5,7,8” 这种 4+1=5,这种情况不符合,所以我重构了一下原图,如下: 

这样,向上为 -5,向下为 +5,向左为 -1,向右为 +1。经过这个转换,差值为 1 的两个数一定在同一行,差值为 5 的两个数一定在同一列。

int b[]={-1,1,-5,+5};  //上下左右4个方向。
for(int i=0;i<=4;i++)  // 第i个数和第j个数是否相邻。
      for(int j=0;j<=4;j++)
        for(int k=0;k<=3;k++)  // k是上下左右4个方向
             if(star[i]+b[k]==star[j])   // i和j在k方向上连通。
                 ...... 

关于判断 5 个数是否连通,我决定用前面学到的 BFS 来处理: 比如现在有一个排列{2, 3, 4, 8, 9}(见下图中的位置),我用 BFS 队列的步骤是:

  •   2 进队列:当前队列是 (2);
  • 2 的邻居进队列:当前队列是 (2, 3);
  • 弹出 2:当前队列是 (3);
  • 3 的邻居进队列:当前队列是 (3, 4, 8);
  • 弹出 3 ;当前队列是 (4, 8)
  • 4 的邻居进队列:当前队列是 (4,8,9)
  • 弹出 4:当前队列是 (8, 9)
  • 8 没有没处理过的邻居了。
  • 弹出 8:当前队列是 (9)
  • 弹出 9;
  • 队列空。

如果 5 个数都进过队列,那么它们就是连通的。

 下面是完整代码:

#include<bits/stdc++.h>
using namespace std;
int star[]={1,2,3,4,6,7,8,9,11,12,13,14};
int num=0; //统计排列的个数
int b[]={-1,1,-5,+5};//上下左右4个方向。
bool bfs(void){
    //star[0]~star[4]这前5个数是递归出来的5个数。用BFS判断它们是否连通
    bool status[5]={false};
     //这5个数的状态,判断其中某个数是否已经用队列处理过
    int p=0;//进队列的个数。如果5个数都进过队列
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值