并查集与拓扑排序
并查集就是开一个数组存每个节点的父节点,然后查找和并两个方法。遇到的题不一样思考方式也不一样。可能就是改变并的方式。并查集是一种用于管理元素所属集合的数据结构,其最终的状态为一个森林。森林中的每棵树表示一个集合,树中的节点表示对应集合中的元素。
拓扑排序(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 + " ");
}
}
}