算法:程序设计之并查集

前言

  之前一直都是听说并查集,感觉是一个神乎其技,狂拽酷炫。却没有想过在自己学习并查集之前,自已在解决问题的时候也能够想到一个和并查集异曲同工的方法。这个还是很愉快的。


版权说明

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:Coding-Naga
发表日期: 2015年9月27日
链接:http://blog.csdn.net/lemon_tree12138/article/details/48690181
来源:CSDN
更多内容:分类 >> 算法与数学


北大OJ实例

原题描述

 

思路分析

  按照上面的题目意思,我们先来看一些示例:D 1 2, D 2 3, D 4 5, D 5 6

  上面的例子里我们可以作这样的一个划分( (1,2,3) (4,5,6) )

  划分的依据是能不能够确定两个案件是否相关。比如我可以确定1和2不是同一个团伙犯案,2和3不是同一个团伙犯案,1和3是同一个团伙犯案。在经过这样的划分之后,在时间的推移下,每个划分的集合是越来越大,集合的个数也是越来越多。

1.构造动态集合

  题目中说,我们需要按照时间的顺序来做逻辑输出。在这种有诸多不能确定事物的情况下,我们很容易就可以想到使用链表的结构来保存。Java中,我们有List类就可以。

2.标记数组

  上面使用了链表的结构来解题。不过,不幸的是在OJ的编译器里不支持List这个结构。在我们提交代码之后,会报出一个编译错误。不过在整个思路中我们有一点是一致的,那就是我们需要把这些案件划分成几个不同的集合。每个集合里面的元素是相关的,是同一个团伙犯的案,不是同一个团伙犯的案。

  那么我们的做法是这样的,首先定义一个大小为案件总数的数组,这个数组是用来标记每个案件是属于哪个集合。在集合内部,有两个互为相反数的标记,是用来标记是不是属于同一个犯罪团伙犯的案。随着时间的推移,我们需要不断修正这些集合。下面会有代码的实现部分。

3.并查集

  基于上面的标记数组。我们可以扩展到并查集的使用。并查集的做法是在每个集合中标记元素的上一级,以后需要比较任意两个案件的关系时,只要比较他们最上面的那个祖先是否是同一个就可以判断他们是否是属于同一个集合。当然,Java中没有C中的指针一说,所以,这里我们还是使用数组来保存标记信息。

  些过并查集的读者应该都知道,在并查集不断扩大的过程中,可能会有并查集的树结构在不断长高。而我们查询的时间复杂度就是和我们的树的高度有关。所以,我们要想一个办法让我们的树结构的高度小下来。这里,我们有两种方法来解决问题。第一种:在每次查找祖先节点时,我们把某些节点先前移动;第二种:我们每次合并树时都必须遵从把小树加到大树中,而不是把大树加到小树上。在下面的代码里,我们使用的是第二种方法来解决树的路径压缩。


代码实现

1.链表实现

public class Main_0 {

    private final static String NOT_SURE_LABEL = "Not sure yet.";
    private final static String DIFFERENT_GANGS_LABEL = "In different gangs.";
    private final static String SAME_GANG_LABEL = "In the same gang.";
    
    private final static int NOT_INTERSECTION = 0; // 000
    private final static int ONE_INTERSECTION_ONE = 2; // 010
    private final static int ONE_INTERSECTION_TWO = 1; // 001
    private final static int TWO_INTERSECTION_ONE = 6; // 110
    private final static int TWO_INTERSECTION_TWO = 5; // 101
    
    private static String[][] getData(Scanner inputScanner, int m) {
        String[][] data = new String[m][3];
        String message = "";
        inputScanner.nextLine();
        for (int i = 0; i < data.length; i++) {
            message = inputScanner.nextLine();
            data[i] = message.split(" ");
        }
        
        return data;
    }
    
    private static int intersection(Determined x, Determined y) {
        for (int i = 0; i < y.oneList.size(); i++) {
            if (x.oneList.contains(y.oneList.get(i))) {
                return ONE_INTERSECTION_ONE;
            }
        }
        
        for (int i = 0; i < y.oneList.size(); i++) {
            if (x.twoList.contains(y.oneList.get(i))) {
                return TWO_INTERSECTION_ONE;
            }
        }
        
        for (int i = 0; i < y.twoList.size(); i++) {
            if (x.oneList.contains(y.twoList.get(i))) {
                return ONE_INTERSECTION_TWO;
            }
        }
        
        for (int i = 0; i < y.twoList.size(); i++) {
            if (x.twoList.contains(y.twoList.get(i))) {
                return TWO_INTERSECTION_TWO;
            }
        }
        
        return NOT_INTERSECTION;
    }
    
    private static void merge(Determined x, Determined y, int type) {
        switch (type) {
        case ONE_INTERSECTION_ONE:
        case TWO_INTERSECTION_TWO:
            for (int i = 0; i < y.getOneList().size(); i++) {
                if (!x.getOneList().contains(y.getOneList().get(i))) {
                    x.addNewOne(y.getOneList().get(i));
                }
            }
            
            for (int i = 0; i < y.getTwoList().size(); i++) {
                if (!x.getTwoList().contains(y.getTwoList().get(i))) {
                    x.addNewTwo(y.getTwoList().get(i));
                }
            }
            break;
            
        case ONE_INTERSECTION_TWO:
        case TWO_INTERSECTION_ONE:
            for (int i = 0; i < y.getOneList().size(); i++) {
                if (!x.getTwoList().contains(y.getOneList().get(i))) {
                    x.addNewTwo(y.getOneList().get(i));
                }
            }
            
            for (int i = 0; i < y.getTwoList().size(); i++) {
                if (!x.getOneList().contains(y.getTwoList().get(i))) {
                    x.addNewOne(y.getTwoList().get(i));
                }
            }
            break;
    
        default:
            break;
        }
    }
    
    private static void arrange(List<Determined> sureList) {
        for (int i = sureList.size() - 1; i >= 0; i--) {
            for (int j = i - 1; j >= 0; j--) {
                int interResult = intersection(sureList.get(j), sureList.get(i));
                if (interResult != NOT_INTERSECTION) {
                    merge(sureList.get(j), sureList.get(i), interResult);
                    sureList.remove(i);
                }
            }
        }
    }
    
    private static void answer(List<Determined> sureList, String one, String two) {
        for (int i = 0; i < sureList.size(); i++) {
            if (sureList.get(i).getOneList().contains(one) && sureList.get(i).getOneList().contains(two)) {
                System.out.println(SAME_GANG_LABEL);
                return;
            } else if (sureList.get(i).getOneList().contains(one) && sureList.get(i).getTwoList().contains(two)) {
                System.out.println(DIFFERENT_GANGS_LABEL);
                return;
            } else if (sureList.get(i).getOneList().contains(two) && sureList.get(i).getTwoList().contains(one)) {
                System.out.println(DIFFERENT_GANGS_LABEL);
                return;
            }
        }
        
        System.out.println(NOT_SURE_LABEL);
    }
    
    private static void printAnswers(String[][] data, int n) {
        List<Determined> sureList = new ArrayList<Main_0.Determined>();
        Determined newDetermined = null;
        for (int i = 0; i < data.length; i++) {
            if (data[i][0].equals("D")) {
                // 先添加到sureList中
                newDetermined = new Main_0.Determined();
                newDetermined.addNewOne(data[i][1]);
                newDetermined.addNewTwo(data[i][2]);
                sureList.add(newDetermined);
                
                // 再整理sureList
                arrange(sureList);
            } else {
                answer(sureList, data[i][1], data[i][2]);
            }
        }
    }
    
    public static void main(String[] args) {
        Scanner inputScanner = new Scanner(System.in);
        int t = inputScanner.nextInt();
        
        while (t-- > 0) {
            int n = inputScanner.nextInt();
            int m = inputScanner.nextInt();
            String[][] data = getData(inputScanner, m);
            printAnswers(data, n);
        }
    }
    
    static class Determined {
        private List<String> oneList = new ArrayList<>();
        private List<String> twoList = new ArrayList<>();
        
        public List<String> getOneList() {
            return oneList;
        }
        
        public void setOneList(List<String> oneList) {
            this.oneList = oneList;
        }
        
        public void addNewOne(String newOne) {
            oneList.add(newOne);
        }
        
        public List<String> getTwoList() {
            return twoList;
        }
        
        public void setTwoList(List<String> twoList) {
            this.twoList = twoList;
        }
        
        public void addNewTwo(String newTwo) {
            twoList.add(newTwo);
        }
    }
}


2.标记数组(并查集前导)

import java.util.Scanner;

public class Main {

    private final static String NOT_SURE_LABEL = "Not sure yet.";
    private final static String DIFFERENT_GANGS_LABEL = "In different gangs.";
    private final static String SAME_GANG_LABEL = "In the same gang.";

    private static String[][] getData(Scanner inputScanner, int m) {
        String[][] data = new String[m][3];
        String message = "";
        inputScanner.nextLine();
        for (int i = 0; i < data.length; i++) {
            message = inputScanner.nextLine();
            data[i] = message.split(" ");
        }

        return data;
    }

    private static void answer(int[] cases, int one, int two) {
        if (cases[one] == 0 || cases[two] == 0) {
            System.out.println(NOT_SURE_LABEL);
            return;
        }

        if (cases[one] == cases[two]) {
            System.out.println(SAME_GANG_LABEL);
        } else if (cases[one] == -cases[two]) {
            System.out.println(DIFFERENT_GANGS_LABEL);
        } else {
            System.out.println(NOT_SURE_LABEL);
        }
    }

    private static void printAnswers(String[][] data, int n) {
        int[] cases = new int[n]; // 记录案件信息(标记犯罪团伙)

        for (int i = 0; i < data.length; i++) {
            if (data[i][0].equals("D")) {
                // 先添加到sureList中
                int one = Integer.valueOf(data[i][1]);
                int two = Integer.valueOf(data[i][2]);
                if (cases[one] == 0 && cases[two] == 0) {
                    cases[one] = i + 1;
                    cases[two] = -(i + 1);
                } else if (cases[one] != 0 && cases[two] == 0) {
                    cases[two] = -cases[one];
                } else if (cases[one] == 0 && cases[two] != 0) {
                    cases[one] = -cases[two];
                } else if (Math.abs(cases[one]) != Math.abs(cases[two])) {
                    int flagCase = cases[two];
                    for (int j = 0; j < cases.length; j++) {
                        if (cases[j] == flagCase) {
                            cases[j] = -cases[one];
                        } else if (cases[j] == -flagCase) {
                            cases[j] = cases[one];
                        }
                    }
                }
            } else {
                answer(cases, Integer.valueOf(data[i][1]),
                        Integer.valueOf(data[i][2]));
            }
        }
    }

    public static void main(String[] args) {
        Scanner inputScanner = new Scanner(System.in);
        int t = inputScanner.nextInt();

        while (t-- > 0) {
            int n = inputScanner.nextInt();
            int m = inputScanner.nextInt();
            String[][] data = getData(inputScanner, m);
            printAnswers(data, n);
        }
    }
}

3.并查集实现

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 并查集
 */
public class Main {

    private final static String NOT_SURE_LABEL = "Not sure yet.";
    private final static String DIFFERENT_GANGS_LABEL = "In different gangs.";
    private final static String SAME_GANG_LABEL = "In the same gang.";

    private static int[] parents = null;
    private static int[] classifys = null;
    private static int[] size = null;

    static BufferedReader reader = new BufferedReader(new InputStreamReader(
            System.in));

    private static void answer(int x, int y) {
        int px = find(x);
        int py = find(y);

        if (px != py) {
            System.out.println(NOT_SURE_LABEL);
        } else {
            if (classifys[x] == classifys[y]) {
                System.out.println(SAME_GANG_LABEL);
            } else {
                System.out.println(DIFFERENT_GANGS_LABEL);
            }
        }
    }

    private static void printAnswers(int m, int n) throws IOException {
        for (int i = 0; i < m; i++) {
            String[] data = reader.readLine().split(" ");

            if (data[0].equals("D")) {
                int x = Integer.valueOf(data[1]);
                int y = Integer.valueOf(data[2]);
                union(x, y);
            } else {
                answer(Integer.valueOf(data[1]), Integer.valueOf(data[2]));
            }
        }
    }

    private static int find(int x) {
        if (parents[x] == x) {
            return x;
        }

        int p = find(parents[x]);

        classifys[x] = (classifys[x] + classifys[parents[x]]) % 2;

        parents[x] = p;

        return p;
    }

    private static void union(int x, int y) {
        int px = find(x);
        int py = find(y);

        if (px != py) {
            if (size[px] <= size[py]) {
                parents[px] = py;
                classifys[px] = (classifys[x] + classifys[y] + 1) % 2;
                size[py] += size[px];
            } else {
                parents[py] = px;
                classifys[py] = (classifys[x] + classifys[y] + 1) % 2;
                size[px] += size[py];
            }
        }
    }

    private static void initFlagArray(int n) {
        parents = new int[n + 1];
        for (int i = 0; i < parents.length; i++) {
            parents[i] = i;
        }

        classifys = new int[n + 1];

        size = new int[n + 1];
        for (int i = 0; i < size.length; i++) {
            size[i] = 1;
        }
    }

    public static void main(String[] args) {
        int t = 0;
        try {
            t = Integer.parseInt(reader.readLine());

            while (t-- > 0) {
                String[] mn = reader.readLine().split(" ");
                int n = Integer.parseInt(mn[0]);
                int m = Integer.parseInt(mn[1]);

                initFlagArray(n);

                printAnswers(m, n);
            }

        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


转载于:https://www.cnblogs.com/fengju/p/6336043.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值