1.Union: 查看两个元素之间是否可连接
- 判断网络中节点连接问题
-
2.并查集的实现:
并查集中并不保存实际元素,而是元素与索引进行一一映射
// UF 接口
public interface UF {
int getSize();
// 查看两个元素可连接
boolean isConnected(int p, int q);
void unionElements(int p, int q);
}
// 并查集使用 Tree 实现
public class UnionFind implements UF{
// index 对应元素,存储的值为集合的id
private int[] id;
// 先初始化传入的并查集大小,之后的再说
public UnionFind(int[] arr){
id = new int[arr.length];
for(int i = 0; i < arr.length; i++){
id[i] = i;
}
}
// 获取size
@Override
public int getSize(){
return id.length;
}
// 查看索引对应的集合
public int find(int index){
if (index < 0 || index > id.length) {
throw new IllegalArgumentException("out of index");
}
return id[index];
}
// 查看两个元素可连接
@Override
public boolean isConnected(int p, int q){
return find(p) == find(q);
}
@Override
// 修改其中一个节点的ID
// 之后需要将所有均改为同一ID
public void unionElements(int p, int q){
int pID = find(p);
int qID = find(p);
if(qId == pID){
return;
}
for(int i = 0; i < id.length(); i++){
if(find[i] == qID){
find[i] = pID;
}
}
}
}
// 这时候 isConnected() 的时间复杂度为O(1)
// 而 unionElement() 为 O(n)3.优化
引入父节点,这样每次就只需要修改父节点对应的上一级 节点
// 注意此时 root 节点对应的上一个父节点就是他本身
public class UnionFind implements UF{
// 存储 索引对应的父节点
private int[] parent;
// 先初始化传入的并查集大小,之后的再说
public UnionFind(int[] arr){
parent = new int[arr.length];
for(int i = 0; i < arr.length; i++){
parent[i] = i;
}
}
// 获取size
@Override
public int getSize(){
return parent.length;
}
// 查看索引对应的集合
public int find(int index){
if (index < 0 || index > id.length) {
throw new IllegalArgumentException("out of index");
}
// 要找到 root ,返回 所属的 root 对应的结合ID
// root 节点的值与自身索引相同
while(index != parent[index]){
index = parent[index];
}
return index;
}
// 查看两个元素可连接
@Override
public boolean isConnected(int p, int q){
return find(p) == find(q);
}
@Override
// 修改其中一个节点的ID
// 之后需要将所有均改为同一ID
public void unionElements(int p, int q){
// 获取父节点
int pParent = find(p);
int qParent = find(q);
// 如果两节点相同
if(pParent == qParent){
return;
}else{
// 其中一个元素的父亲节点变指向另一个元素
parent[p] = qParent;
}
}
}
// 此时 find() 的 时间复杂度为 O(h)
// unionElements() 的时间复杂度为 O(h)使用 size() 进行优化
由于时间复杂度与树的高度有关,所以size() 最好
// 注意此时 root 节点对应的上一个父节点就是他本身
public class UnionFind implements UF{
// 存储 索引对应的父节点
private int[] parent;
private int[] sz;
// 先初始化传入的并查集大小,之后的再说
public UnionFind(int[] arr){
parent = new int[arr.length];
for(int i = 0; i < arr.length; i++){
parent[i] = i;
sz[i] = 1;
}
}
// 获取size
@Override
public int getSize(){
return parent.length;
}
// 查看索引对应的集合
public int find(int index){
if (index < 0 || index > id.length) {
throw new IllegalArgumentException("out of index");
}
// 要找到 root ,返回 所属的 root 对应的结合ID
// root 节点的值与自身索引相同
while(index != parent[index]){
index = parent[index];
}
return index;
}
// 查看两个元素可连接
@Override
public boolean isConnected(int p, int q){
return find(p) == find(q);
}
@Override
// 修改其中一个节点的ID
// 之后需要将所有均改为同一ID
public void unionElements(int p, int q){
// 获取父节点
int pParent = find(p);
int qParent = find(q);
// 如果两节点相同
if(pParent == qParent){
return;
}else{
if(sz[qParent] > sz[pParent]){
sz[qParent] += sz[pParent];
parent[pParent] = qParent;
}else{
sz[pParent] += sz[qParent];
parent[qParent] = pParent;
}
}
}
}
使用 rank()树的高度优化
blic class UnionFind implements UF{
// 存储 索引对应的父节点
private int[] parent;
private int[] rank;
// 先初始化传入的并查集大小,之后的再说
public UnionFind(int[] arr){
parent = new int[arr.length];
rank = new int[arr.length];
for(int i = 0; i < arr.length; i++){
parent[i] = i;
rank[i] = 1;
}
}
// 获取size
@Override
public int getSize(){
return parent.length;
}
// 查看索引对应的集合
public int find(int index){
if (index < 0 || index > id.length) {
throw new IllegalArgumentException("out of index");
}
// 要找到 root ,返回 所属的 root 对应的结合ID
// root 节点的值与自身索引相同
while(index != parent[index]){
index = parent[index];
}
return index;
}
// 查看两个元素可连接
@Override
public boolean isConnected(int p, int q){
return find(p) == find(q);
}
@Override
// 修改其中一个节点的ID
// 之后需要将所有均改为同一ID
public void unionElements(int p, int q){
// 获取父节点
int pParent = find(p);
int qParent = find(q);
// 如果两节点相同
if(pParent == qParent){
return;
}else{
if(rank[qParent] > rank[pParent]){
parent[pParent] = qParent;
}else if(rank[qParent] < rank[pParent]){
parent[qParent] = pParent;
}else{
parent[pRoot] = qRoot;
rank[qRoot] +=1;
}
}
}
}
直接让 索引的上移节点指向 root 节点,减少高度 , 进行路径压缩
- 让同一高度下,子节点数较多
// 在查找过程中进行的优化
public int find(int index){
if (index < 0 || index > id.length) {
throw new IllegalArgumentException("out of index");
}
// 要找到 root ,返回 所属的 root 对应的结合ID
// root 节点的值与自身索引相同
while(index != parent[index]){
parent[index] = parent[parent[index]];
index = parent[index];
}
return index;
}