并查集

1.设计算法的步骤

  • 给问题建模
  • 设计算法解决
  • 满足速度和内存要求?
  • 不满足,找出原因
  • 寻找更好的方法
  • 迭代,直到满意

2.问题描述

有一系列的物体,每两个物体之间相连,问题是:给出任意两个物体,是否有一条路连接其中两个物体?

3.问题建模

性质:

  • 自身性:p与p自身相连
  • 对称性:如果p与q相连,则q与p相连
  • 传递性:如果p与q相连,q与r相连,则p与r相连

连通集:连通集中的任意两个对象都是相连接的。

确定方法:连接操作、是否连接判断

4.快速查找

数组作为该问题的数据结构,数组中每一个元素对应一个物体。数组的下标唯一确定物体(比如说第五个数下标为5,代表着第五号物体),相同数值的物体为相连物体(比如第2个数和第5个数值都为1,则这两个数相连)。

算法初始化连接查询
快速查找NN1

评价:如果使用快速查找算法,初始化加上连接需要N^2时间,我们不能接受平方时间的算法。

5.快速合并

以数组方式储存一个数,角标还是物体的“身份证”,值对应着父节点。查找只需判断p和q是否有同一个根节点,合并只需将p的根节点变为q的根节点的父节点。

在这里插入图片描述

由于可能生成很瘦很高的树,在连接和查询时效率比较低,为N。

算法初始化连接查询
快速合并NNN

6.带权重的快速合并

为了避免生成很高的树,我们要避免把高树连接在矮树的根节点上,而应该把矮树连接在高树的根节点上。操作上,实时记录树的节点总数,根据节点总数大小来判断树的高矮。如图可以看出,本例中,带权重的快速合并树高控制在4以内。

在这里插入图片描述

算法初始化连接查询
带权重的快速合并Nlg Nlg N

7.代码(Java)

github代码

public class UF{
    public static void main(String[] args){
        StdOut.println("请输入节点个数");
        int N = StdIn.readInt();
        //***********在此选择算法***********//
        // QuickFind uf = new QuickFind(N);
        // QuickUnion uf = new QuickUnion(N);
        WeightedQuickUnion uf = new WeightedQuickUnion(N);
        //********************************//
        StdOut.println("请输入节点");
        while(!StdIn.isEmpty()){
            int p = StdIn.readInt();
            int q = StdIn.readInt();
            if(q>N-1 || p>N-1) {
                StdOut.println("超出范围,请重新输入!");
                continue;
            }
            if(!uf.connected(p, q)){
                StdOut.println("未连接");
                uf.union(p, q);
            }else{StdOut.println("已连接");}
        }
    }
}

class QuickFind {
    private int[] id;
    public QuickFind(int N){
            id = new int[N];
            for(int i=0;i<N;i++){
                id[i]=i;
            }
        }
    void union(int p, int q){
        int pid = id[p];
        int qid = id[q];
        for(int i = 0;i<id.length;i++){
            if(id[i]==pid) id[i] = qid;
        }
    };
    boolean connected(int p, int q){
        return id[p] == id[q];
    };
    int find(int p){
        return id[p];
    };
    int count(){
        return id.length;
    };
}

class QuickUnion {//初始化
    private int[] id;
    public QuickUnion(int N){
        id = new int[N];
        for(int i=0;i<N;i++){
            id[i]=i;
        }
    }
    private int root(int i){
        while(i!=id[i]) i = id[i];
        return i;
    }
    void union(int p, int q){
        int j  = root(p);
        int k = root(q);
        id[j] = k; //j是k的子树
    };
    boolean connected(int p, int q){
        return root(p)==root(q);
    };
    int count(){
        return id.length;
    };
}


class WeightedQuickUnion {//初始化
    private int[] id;
    private int[] sz;//储存树的大小
    public WeightedQuickUnion(int N){
        id = new int[N];
        for(int i=0;i<N;i++){
            id[i]=i;
        }
        sz = new int[N];
    }
    private int root(int i){
        while(i!=id[i]) i = id[id[i]]; //带路径压缩,两层压缩最小树高为2,三层压缩最小数高为3
        return i;
    }
    void union(int p, int q){
        int j  = root(p);
        int k = root(q);
        if(sz[p]>sz[q]){id[p]=k;sz[p]+=sz[q];} //q树连接到p数上
        else{id[q]=j;sz[q]+=sz[p];}

    };
    boolean connected(int p, int q){
        return root(p)==root(q);
    };
    int count(){
        return id.length;
    };
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值