并查集与拓扑排序(题目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 + " ");
        }

    }
}
相关推荐
要开心吖ZSH5 分钟前
《Spring 中上下文传递的那些事儿》Part 4:分布式链路追踪 —— Sleuth + Zipkin 实践
java·分布式·spring
桦说编程24 分钟前
深入解析CompletableFuture源码实现
java·性能优化·源码
傻啦嘿哟26 分钟前
Python 办公实战:用 python-docx 自动生成 Word 文档
开发语言·c#
翻滚吧键盘30 分钟前
js代码09
开发语言·javascript·ecmascript
q5673152337 分钟前
R语言初学者爬虫简单模板
开发语言·爬虫·r语言·iphone
蓝澈11211 小时前
迪杰斯特拉算法之解决单源最短路径问题
java·数据结构
Kali_071 小时前
使用 Mathematical_Expression 从零开始实现数学题目的作答小游戏【可复制代码】
java·人工智能·免费
rzl021 小时前
java web5(黑马)
java·开发语言·前端
时序数据说1 小时前
为什么时序数据库IoTDB选择Java作为开发语言
java·大数据·开发语言·数据库·物联网·时序数据库·iotdb
jingling5552 小时前
面试版-前端开发核心知识
开发语言·前端·javascript·vue.js·面试·前端框架