java并查集_并查集 Java实现

关于并差集笔者也实在不想扯那么多理论,代码这个东西越扯理论越糊涂,自己实践才会知道要点在哪里。

一、并差集概念

并查集就分为两个操作一个并一个查,通常这种题都是分类的题,类间元素都是有关系的,类外元素没有关系。题目一定会给出两个元素之间的关系,这时我们就查find(),如果两个元素在一个集合里面就什么都不作;如果两个元素不在一个集合里,我们将这两个元素所在集合合并,因为这个集合里面的元素都是相互之间有关系的。所以这样的题目,找到元素之间存在的关系就是题眼,关系就是分类的依据。

二、并差集操作

1、双亲表示法

这里我们采用双亲表示法作为实现并差集的数据结构,双表示法即每个结点要记录自己的父结点。

并差集随不同的数据结构会有不同的实现,所以还是那句话思想最重要。

//使用双亲表示法作为存储结构

public classNode {publicString data;public intparent;

}

2、初始化

privateNode[] ufs;

UFoperation(String[] datas) {

ufs= newNode[datas.length];for(int i = 0; i < datas.length; i++) {

ufs[i]= newNode();

ufs[i].data=datas[i];

ufs[i].parent= -1;

}

}

3、查操作

(1)常规查操作

intfind(String data) {int pos = -1;for(int i = 0; i < ufs.length; i++) {if(ufs[i].data==data) {

pos=i;break;

}

}while(ufs[pos].parent!=-1) {

pos=ufs[pos].parent;

}returnpos;

}

算法思想:就是找树的根节点啊

时间复杂度:O(h) h为树高

(2)带路径压缩的查操作

算法思想:我们每做一次查操作,就把待查结点以及其祖先结点(除根结点)都直接连在根结点之上,就是最终的树树高只有2,结点全部连到根结点上。

intfind(String data) {int pos = -1;int mark = -1;int root = -1;int pre = -1;for(int i = 0; i < ufs.length; i++) {if(ufs[i].data==data) {

pos=i;break;

}

}

mark=pos;while(ufs[pos].parent!=-1) {

pos=ufs[pos].parent;

}

root=pos;

pos=mark;//这里根节点一定不可以进来,因为使用了前置指针,会下标出界。

while(ufs[pos].parent!=-1) {

pre=ufs[pos].parent;

ufs[pos].parent=root;

pos=pre;

}returnroot;

}

时间复杂度:O(1)

4、并操作

算法思想:直接把一棵树根结点的父指针指向另一棵树的根结点

booleanmerge(String s1, String s2) {int p1 =find(s1);int p2 =find(s2);if(p1==p2) return false;else{

ufs[p2].parent=p1;return true;

}

}

时间复杂度:O(1)

完整代码:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public classUFoperation {privateNode[] ufs;

UFoperation(String[] datas) {

ufs= newNode[datas.length];for(int i = 0; i < datas.length; i++) {

ufs[i]= newNode();

ufs[i].data=datas[i];

ufs[i].parent= -1;

}

}/*int find(String data) {

int pos = -1;

for(int i = 0; i < ufs.length; i++) {

if(ufs[i].data==data) {

pos = i;

break;

}

}

while(ufs[pos].parent!=-1) {

pos = ufs[pos].parent;

}

return pos;

}*/

//带路径压缩的

intfind(String data) {int pos = -1;int mark = -1;int root = -1;int pre = -1;for(int i = 0; i < ufs.length; i++) {if(ufs[i].data==data) {

pos=i;break;

}

}

mark=pos;while(ufs[pos].parent!=-1) {

pos=ufs[pos].parent;

}

root=pos;

pos=mark;//这里根节点一定不可以进来,因为使用了前置指针,会下标出界。

while(ufs[pos].parent!=-1) {

pre=ufs[pos].parent;

ufs[pos].parent=root;

pos=pre;

}returnroot;

}booleanmerge(String s1, String s2) {int p1 =find(s1);int p2 =find(s2);if(p1==p2) return false;else{

ufs[p2].parent=p1;return true;

}

}public static voidmain(String[] args) {

String[] input= new String[] {"A","B","C","D","E","F","G","H","I"};

UFoperation u= newUFoperation(input);

String[][] test= {{"A","B"},{"A","C"},{"D","E"},{"D","F"},{"D","G"},{"H","I"}};for(int i = 0; i < test.length; i++) {

u.merge(test[i][0],test[i][1]);

}for(String s : input) {

System.out.print(u.find(s)+ " ");

}

}

}

View Code

5、带权路径

classSolution {private HashMap SS = new HashMap<>();private HashMap w = new HashMap<>();//记录到父节点的权值

public double[] calcEquation(List> equations, double[] values, List>queries) {

init(equations);double[] ans = new double[queries.size()];for(int i = 0; i < values.length; i++) {

List list =equations.get(i);

merge(list.get(0),list.get(1),values[i]);

}for(int i = 0; i < queries.size(); i++) {

List list =queries.get(i);if(find(list.get(1))==find(list.get(0))&&SS.containsKey(list.get(0)))

ans[i]= getRootValue(list.get(1))/getRootValue(list.get(0));elseans[i]= -1.0;

}returnans;

}private void init(List>equations) {for(Listlist : equations) {for(String s : list) {if(!SS.containsKey(s)) {

SS.put(s,s);

w.put(s,1.0);

}

}

}

}privateString find(String s1) {while(SS.get(s1)!=s1) {

s1=SS.get(s1);

}returns1;

}//求到根节点的权值

private doublegetRootValue(String s) {double value = 1.0;while(SS.get(s)!=s) {

value= value*(w.get(s));

s=SS.get(s);

}returnvalue;

}private void merge(String s1, String s2, doublevalue) {

String p1=find(s1);

String p2=find(s2);if(p1==p2) return;

SS.put(p2,p1);

w.put(p2,(getRootValue(s1)*value)/getRootValue(s2));

}

}

像这样的题要在40钟之内完成还是有压力的鸭。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值