并查集与拓扑排序(题目JAVA)

并查集与拓扑排序

并查集就是开一个数组存每个节点的父节点,然后查找和并两个方法。遇到的题不一样思考方式也不一样。可能就是改变并的方式。并查集是一种用于管理元素所属集合的数据结构,其最终的状态为一个森林。森林中的每棵树表示一个集合,树中的节点表示对应集合中的元素。
拓扑排序(Topological sorting)要解决的问题是如何给一个有向无环图的所有节点排序。

1.蓝桥幼儿园(模版题)

java 复制代码
package com.js.datastructure.recursion.蓝桥.国特训练营.并查集;

import java.util.Scanner;

public class 蓝桥幼儿园 {
    static int [] pa;
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int n = scanner.nextInt();
        int m = scanner.nextInt();
        pa = new int[n+1];

        //建立父节点数组
        for (int i = 1; i < n+1; i++) {
            pa[i] = i;
        }

        for (int i = 0; i < m; i++) {
            int op = scanner.nextInt();
            int x = scanner.nextInt();
            int y = scanner.nextInt();
            if(op == 1){
                union(x,y);
            }else {
                if(x == y){
                    System.out.println("YES");
                }else {
                    int x_pa = find(x);
                    int y_pa = find(y);
                    if(x_pa == y_pa){
                        System.out.println("YES");
                    }else {
                        System.out.println("NO");
                    }
                }
            }
        }

    }

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

    static void union(int x, int y){
        int x_pa = find(x);
        int y_pa = find(y);

        if(x_pa != y_pa){
            pa[y_pa] = x_pa;
        }
    }
}

2.关押罪犯


这个题有点不太好理解,就是并的方式不太一样,查的话都一样。

互不交集的一对一对没有影响,有影响的是有交集的,

比如例子

1 2

3 4

1 3

2 3

1 4

2 4

这是按照影响排序后的,前两个如果此时就直接确定在那个监狱,第三个就就会受影响,所以需要存的是每次只需要知道这两个不在一个监狱,后面可以把监狱合并,这是这个题的并。

java 复制代码
package com.js.datastructure.recursion.蓝桥.国特训练营.并查集;

import java.util.*;

public class 关押罪犯 {
    static int pa[];
    static class Edge{
        int a;
        int b;
        int c;
        public Edge(int a,int b,int c){
            this.a = a;
            this.b = b;
            this.c = c;
        }
    }

    static int find(int x){
        if(pa[x] == x){
            return x;
        }
        pa[x] = find(pa[x]);
        return pa[x];
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();

        pa = new int[2*n+2];
        for (int i = 0; i < pa.length; i++) {
            pa[i] = i;
        }
        ArrayList<Edge> dd = new ArrayList<>();
        for (int i = 0; i < m; i++) {
            dd.add(new Edge(scanner.nextInt(),scanner.nextInt(),scanner.nextInt()));
        }
        Collections.sort(dd,(Edge o1,Edge o2) -> Long.compare(o2.c,o1.c));
        for (Edge ddd: dd){
            int a = ddd.a;
            int b = ddd.b;
            int c = ddd.c;

            int pa_a = find(a);
            int pa_b = find(b);

            if(pa_a == pa_b){
                System.out.println(c);
                return;
            }
            pa[pa_a] = find(b+n);
            pa[pa_b] = find(a+n);

        }
        System.out.println(0);

    }
}

3.发现环(拓扑排序)


这个题利用链式前向星存边,然后依次去掉入度为1的边,得到最后入度为2的点就是环,然后利用ArrayList存这些点,排序输出。

java 复制代码
package com.js.datastructure.recursion.蓝桥.国特训练营.并查集;

import java.util.*;

public class 发现环 {

    static int [] pa;
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int n = scanner.nextInt();

        //记录入度
        //存储边
        ArrayList<Integer>[] ed = new ArrayList[n+1];
        //初始化
        for (int i = 0; i < n + 1; i++) {
            ed[i] = new ArrayList<>();
        }

        int[] de = new int[n+1];

        for (int i = 0; i < n; i++) {
            int a = scanner.nextInt();
            int b = scanner.nextInt();
            //两边的入度都增加
            de[a]++;
            de[b]++;
            ed[a].add(b);
            ed[b].add(a);
        }

        //队列来优化入度为1的节点
        Deque<Integer> qu = new ArrayDeque<>();
        for (int i = 1; i < n+1; i++) {
            if(de[i] == 1){
                qu.offer(i);
            }
        }

        while (!qu.isEmpty()){
            int t = qu.pollFirst();
            de[t]--;
            for (int z : ed[t]){
                de[z]--;
                if(de[z] == 1){
                    qu.addLast(z);
                }
            }
        }


        ArrayList<Integer> doo = new ArrayList<>();
        for (int i = 1; i < n+1; i++) {
            if(de[i] == 2){
                doo.add(i);
            }
        }
        Collections.sort(doo);
        for (int dooo:doo) {
            System.out.print(dooo + " ");
        }

    }
}

4.最小字典序排列(拓扑排序)


这个题加了一个最小的限制条件,在使用队列的时候使用优先队列。

java 复制代码
package com.js.datastructure.recursion.蓝桥.国特训练营.并查集;

import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Scanner;

public class 最小字典序排列 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int n = scanner.nextInt();
        int m = scanner.nextInt();

        //就是拓扑排序,有向无环图的排序。
        //链式前向星存储边,数组存入度出度。
        int[] de = new int[n+1];
        ArrayList<Integer> [] ar = new ArrayList[n+1];
        //初始化
        for (int i = 0; i < n + 1; i++) {
            ar[i] = new ArrayList();
        }

        for (int i = 0; i < m; i++) {
            int a = scanner.nextInt();
            int b = scanner.nextInt();
            de[b]++;
            ar[a].add(b);
        }

        //删除掉入度为0的点,依次进行,因为要求字典序最小所以用优先队列,默认小根堆
        PriorityQueue<Integer> qu = new PriorityQueue<>();
        for (int i = 1; i < n+1; i++) {
            if(de[i] == 0){
                qu.offer(i);
            }
        }

        ArrayList<Integer> dd = new ArrayList<>();
        while (!qu.isEmpty()){
            int t = qu.poll();
            dd.add(t);
            for (int z : ar[t]){
                de[z]--;
                if(de[z] == 0){
                    qu.add(z);
                }
            }
        }

        for (int i = 1; i < n + 1; i++) {
            if(de[i] != 0){
                System.out.println(-1);
                return;
            }
        }
        for (int z : dd){
            System.out.print(z + " ");
        }

    }
}
相关推荐
硅的褶皱7 分钟前
对比分析LinkedBlockingQueue和SynchronousQueue
java·并发编程
MoFe111 分钟前
【.net core】天地图坐标转换为高德地图坐标(WGS84 坐标转 GCJ02 坐标)
java·前端·.netcore
季鸢41 分钟前
Java设计模式之观察者模式详解
java·观察者模式·设计模式
Fanxt_Ja1 小时前
【JVM】三色标记法原理
java·开发语言·jvm·算法
蓝婷儿1 小时前
6个月Python学习计划 Day 15 - 函数式编程、高阶函数、生成器/迭代器
开发语言·python·学习
love530love1 小时前
【笔记】在 MSYS2(MINGW64)中正确安装 Rust
运维·开发语言·人工智能·windows·笔记·python·rust
Mr Aokey2 小时前
Spring MVC参数绑定终极手册:单&多参/对象/集合/JSON/文件上传精讲
java·后端·spring
slandarer2 小时前
MATLAB | 绘图复刻(十九)| 轻松拿捏 Nature Communications 绘图
开发语言·matlab
狐凄2 小时前
Python实例题:Python计算二元二次方程组
开发语言·python
小马爱记录2 小时前
sentinel规则持久化
java·spring cloud·sentinel