算法:并查集

操作

  1. 合并(Union):把两个不相交的集合合并为一个集合;
  2. 查询(Find):查询两个元素是否在同一个集合中。

并查集

重要思想:用集合中的一个元素代表集合

快速查询 quick find

/**
 * 并查集
 * 快速查找
 * id数组存放集合元素
 */
public class QuickFindTest {

    private static boolean find(int[] id, int p, int q) {
        return id[p] == id[q];
    }

    private static int[] union(int[] id, int p, int q) {
        if (find(id, p ,q)) {
            return id;
        }

        int idp = id[p];
        for (int i = 0; i < id.length; i++) {
            if (id[i] == idp) {
                id[i] = id[q];
             }
        }
        return id;
    }
}

find操作时间复杂度为O(1),而union操作时间复杂度为O(n)

快速合并 quick union

quick find算法的union操作时间复杂度较高,因此改变id数组存放数据的含义,有以下quick union算法

/**
 * 并查集
 * 快速合并
 * id数组存放父元素下标
 */
public class QuickUnionTest {

    private static boolean find(int[] id, int p, int q) {
        return root(id, p) == root(id, q);
    }

    private static int root(int[] id, int i) {
        while (id[i] != i) {
            i = id[i];
        }
        return i;
    }

    private static int[] union(int[] id, int p, int q) {
        int i = root(id, p);
        int j = root(id, q);
        id[i] = j;
        return id;
    }
}

find操作时间复杂度为O(logn),而union操作时间复杂度为O(logn)

按秩合并的快速合并 rank quick union

quick union算法平均复杂度虽然有O(logn),但是极端情况下树会退化为链表,复杂度会退化为O(n),因此,为了减小树的深度,在union时,按树的秩合并

/**
 * 并查集
 * 快速合并
 * id数组存放父元素下标
 * rank数组存放以i为根节点的树高
 */
public class RankQuickUnionTest {

    private static boolean find(int[] id, int p, int q) {
        return root(id, p) == root(id, q);
    }

    private static int root(int[] id, int i) {
        while (id[i] != i) {
            i = id[i];
        }
        return i;
    }

    private static int[] union(int[] id, int[] rank, int p, int q) {
        int i = root(id, p);
        int j = root(id, q);
        if (rank[i] >= rank[j]) {
            id[j] = i;
        } else {
            id[i] = j;
        }
        //当树高一致时,这时树高要加1
        if (rank[i] == rank[j] && i != j) {
            rank[i] ++;
        }
        return id;
    }
}

路径压缩

即便union操作时考虑到树高,按秩合并,树高仍然也可能有log(n),对于root操作,复杂度依然是log(n),因此需要压缩路径来进一步减小树高;

如何进行路径压缩:将父节点直接置为根节点;

路径压缩的时机:root操作时

/**
 * 并查集
 * 并查
 * id数组存放根元素下标
 * rank数组存放以i为根节点的树高
 *
 * 增加路径压缩功能
 */
public class UnionFindTest {

    private static boolean find(int[] id, int p, int q) {
        return root(id, p) == root(id, q);
    }

    private static int root(int[] id, int i) {
        return id[i] == i ? i : (id[i] = root(id, id[i]));
    }

    private static int[] union(int[] id, int[] rank, int p, int q) {
        int i = root(id, p);
        int j = root(id, q);
        if (rank[i] >= rank[j]) {
            id[j] = i;
        } else {
            id[i] = j;
        }
        if (rank[i] == rank[j] && i != j) {
            rank[i] ++;
        }
        return id;
    }
}


参考:
图解:什么是并查集?
算法学习笔记(1) : 并查集

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值