最小生成树和并查集

并查集

并查集,其实就是将树合并,我们让两棵树合并,就可以直接让根节点合并
代码实现(题目:luogu.com.cn/problem/P3367)

#include<bits/stdc++.h>

using namespace std;

const int N = 2e5 + 5;

int n, m;
int f[N];//记录每个节点的父亲节点

int find(int x){//查找x的根节点
  if(!f[x]){
    return x;
  }
  return f[x] = find(f[x]);
}

void merge(int x, int y){//合并x和y
  int _x = find(x), _y = find(y);
  if(_x != _y){
    f[_x] = _y;
  }
}

int main(){
  ios::sync_with_stdio(0);//ios优化
  cin.tie(0);
  cin >> n >> m;
  for(int i = 1; i <= m; i++){
    int op, x, y;
    cin >> op >> x >> y;
    if(op == 1){
      merge(x, y);//合并
    }
    else{
      int xx = find(x), yy = find(y);
      if(xx == yy){//如果是同一根节点就连同
        cout << "Y";
      }
      else cout << "N";
      cout << "\n";
    }
  }
  return 0;
}

并查集有两种优化方法:
1.路径压缩
问题:
如果每次我们都要老老实实的搜一次,那么就会遇到一种卡常数据:书刚好成了一条链,这样我们就会每一次查询根节点都要跳很多层,就会使时间变长。
解决方法:
没搜一次都把搜过路径上的点的父亲都改成根节点,那下次搜到这个点就只用上一层了。
2.按致合并
问题:
如果你不限定合并的时候按什么合并,那就很容易让你TLE,因为Hacker可以让你一直用一棵大树的根结点的父亲改为小树根节点,那树就会不断升高,这样你查找的时候又要跳很多层,使你TLE。
方法:
让小树的父亲改成大树的根节点
(附加)证明:
如果给你这两棵树你会怎么合并呢?
image
(1).如果将1号节点的父亲改为5的话,那5和6不变,1、2、3、4向上跳的次数都要增加1。
(2).如果将5号节点的父亲改为1的话,那1、2、3、4不变,5和6向上跳的次数都要增加1。
显然,做法(2)优
最少层数分析:
假设你是一个Hacker,你要卡一个用了按致合并的并查集代码,那你要想尽办法的使他的层数增加
一开始你有两个节点,第一次层数增加一要这样合并:
image
使层数再增加一层,就需要一颗一样的树,合并完是这样:
image
使层数再再增加一层,就又需要一颗一样的树,合并完是这样:
image
那么最多也只能增加大 \(log(n)\) (n为节点数)

最小生成树

一.kruskal

kruskar是一种以变为主要的最小生成树算法,我们要让一棵树的边权之和最小,即让我们选的每条边最小,那我们可以考虑将边权排序,如果这条边连接的点还不连通,则这条边可以连,如果连了,则这条边连了也没有任何意义,还会使答案变大。
那现在我们考虑如何判断两个点是否联通,这就需要并查集
代码:()

二.prim(敬请期待)

posted @ 2023-08-21 18:22  libohan0518  阅读(11)  评论(0编辑  收藏  举报