码迷,mamicode.com
首页 > 编程语言 > 详细

并差集 Java实现

时间:2019-11-15 20:23:50      阅读:63      评论:0      收藏:0      [点我收藏+]

标签:求值   ref   false   close   数据结构   print   存储结构   private   ++   

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

一、并差集概念

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

二、并差集操作

1、双亲表示法

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

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

//使用双亲表示法作为存储结构
public class Node {
    public String data;
    public int parent;
}

2、初始化

private Node[] ufs;
UFoperation(String[] datas) {
    ufs = new Node[datas.length];
    for(int i = 0; i < datas.length; i++) {
        ufs[i] = new Node();
        ufs[i].data = datas[i];
        ufs[i].parent = -1;
    }
}

3、查操作

(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;
}

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

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

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

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

int find(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;
    }
    return root;
}

时间复杂度:O(1)

4、并操作

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

boolean merge(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)

 

完整代码:

技术图片
public class UFoperation {
    private Node[] ufs;
    UFoperation(String[] datas) {
        ufs = new Node[datas.length];
        for(int i = 0; i < datas.length; i++) {
            ufs[i] = new Node();
            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;
    }*/
    //带路径压缩的
    int find(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;
        }
        return root;
    }
    boolean merge(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 void main(String[] args) {
        String[] input = new String[] {"A","B","C","D","E","F","G","H","I"};
        UFoperation u = new UFoperation(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、带权路径

399. 除法求值

class Solution {
    private HashMap<String,String> SS = new HashMap<>();
    private HashMap<String,Double> w = new HashMap<>();//记录到父节点的权值
    public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        init(equations);
        double[] ans = new double[queries.size()];
        for(int i = 0; i < values.length; i++) {
            List<String> list = equations.get(i);
            merge(list.get(0),list.get(1),values[i]);
        }
        for(int i = 0; i < queries.size(); i++) {
            List<String> 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));
            else
                ans[i] = -1.0;
        }
        return ans;
    }
    private void init(List<List<String>> equations) {
        for(List<String> list : equations) {
            for(String s : list) {
                if(!SS.containsKey(s)) {
                    SS.put(s,s);
                    w.put(s,1.0);
                }
            }
        }
    }
    private String find(String s1) {
        while(SS.get(s1)!=s1) {
            s1 = SS.get(s1);
        }
        return s1;
    }
    //求到根节点的权值
    private double getRootValue(String s) {
        double value = 1.0;
        while(SS.get(s)!=s) {
            value = value*(w.get(s));
            s = SS.get(s);
        }
        return value;
    }
    private void merge(String s1, String s2, double value) {
        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钟之内完成还是有压力的鸭。

并差集 Java实现

标签:求值   ref   false   close   数据结构   print   存储结构   private   ++   

原文地址:https://www.cnblogs.com/mgblogs/p/11869140.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!