前言

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

版权说明

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者: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; // 000private final static int ONE_INTERSECTION_ONE = 2; // 010private final static int ONE_INTERSECTION_TWO = 1; // 001private final static int TWO_INTERSECTION_ONE = 6; // 110private final static int TWO_INTERSECTION_TWO = 5; // 101private 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);// 再整理sureListarrange(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();}}
}

算法:程序设计之并查集相关推荐

  1. 【算法训练营】 - ⑩ 并查集与图

    [算法训练营] - ⑩ 并查集与图 并查集 并查集特征 并查集的优化 图 图结构的表达 图的面试题如何搞定? 图的数据结构 点 边 图 生成图 图算法 广度优先遍历 深度优先遍历 图的拓扑排序算法 最 ...

  2. 基于C语言,详解Kruskal算法(利用并查集)实现构建最小生成树

    目录 一.Kruskal算法的基本介绍 具体做法:找出森林中连接任意两棵树的所有边中,具有最小权值的边,如果将它加入生成树中不产生回路,则它就是生成树中的一条边.这里的关键就是如何判断"将它 ...

  3. mst算法matlab_基于并查集+Kruskal算法的matlab程序及最小生成树绘图

    学了一天最小生成树,稍稍总结一下,这是第一篇 kruskal算法 关于kruskal算法已有大量的资料,不再赘述,算法流程为: 得到邻接矩阵和权值: 初始化,连接距离最小的两点: 连接距离次小的两点, ...

  4. 算法与数据结构——并查集

    文章推荐:[算法与数据结构]-- 并查集 例子: 数据结构--最小生成树之克鲁斯卡尔算法(Kruskal) 1.2 并查集思想(重点) 我们可以把每个连通分量看成一个集合,该集合包含了连通分量的所有点 ...

  5. Kruskal算法:贪心+并查集=最小生成树

    http://www.51nod.com/ Kruskal算法的高效实现需要一种称作并查集的结构.我们在这里不介绍并查集,只介绍Kruskal算法的基本思想和证明,实现留在以后讨论. Kruskal算 ...

  6. 【数据结构与算法基础】并查集原理、封装实现及例题解析(C和java)

    前言 数据结构,一门数据处理的艺术,精巧的结构在一个又一个算法下发挥着他们无与伦比的高效和精密之美,在为信息技术打下坚实地基的同时,也令无数开发者和探索者为之着迷. 也因如此,它作为博主大二上学期最重 ...

  7. 算法高级部分--并查集

    并查集 1.并查集是什么? 2.算法步骤代码 2.1 初始化 2.2 查找祖宗并统一祖宗 2.3合并操作 3.经典例题 1.并查集是什么? 用一个例子来说明... 江湖不是打打杀杀,是人情世故.在一个 ...

  8. 关于算法中的并查集,写的很有意思,转过来看看~

    文章来自于:http://blog.csdn.net/niushuai666/article/details/6662911 例子就是杭电上的畅通工程: http://acm.hdu.edu.cn/s ...

  9. 【C语言】算法学习·种类并查集

    一般的并查集,维护的是具有连通性.传递性的关系,例如亲戚的亲戚是亲戚.但是,有时候,我们要维护另一种关系:敌人的敌人是朋友.种类并查集就是为了解决这个问题而诞生的. 我们先来看一个例题: 其实很容易想 ...

最新文章

  1. 全国计算机等级考试题库二级C操作题100套(第25套)
  2. 数据库PowerDesigner创建图表(模块表分类)
  3. Tips--Docker常用命令
  4. JAVA中request.getParameterMap()用法笔记
  5. 论肱二头肌在日常生活中的锻炼的持久战|健身达人
  6. Archlinux 试用
  7. MyEclipse 6.0 注册码
  8. 《应用时间序列分析:R软件陪同》——2.11 习题
  9. PHP获取grpc请求时间,Go gRPC进阶-超时设置(六)
  10. 微信小程序的弹窗提示
  11. 【Proteus仿真】BME280温湿度气压传感器数据串口输出
  12. 双稳态(bistable)与单稳态
  13. 妙!云服务器远程登录,本地听声音
  14. BCD码、8421码、余三码、格雷码
  15. r语言进行go富集分析_好用的在线GO富集分析工具
  16. 14个以春天为主题的网页设计
  17. 软考信息系统项目管理师质量管理高分论文赏析
  18. python杨辉三角 简单方法
  19. 弘辽科技:新店如何操作才能打爆。
  20. iphone develop tips

热门文章

  1. Kubernetes中Pod的生命周期
  2. 【django】使用虚拟环境
  3. python报错:UnicodeDecodeError: ‘ascii‘ codec can‘t decode byte 0xe0 in position 0: ordinal not in rang
  4. 剖析boot.img的制作流程
  5. 2021天翼杯 密码官方wp
  6. boost库安装编译指南
  7. JAVASE——File类
  8. 2021年1月10日停电感慨
  9. 2020-11-25(多级页表的补充)
  10. 5、时间日期的处理:Java Date类、Calendar类详解