并查集(Java)
并查集常常用来判断在一个图中是否存在回路(是否可以生成树),以及用来判断图的联通性问题。
这里介绍并查集的一种简单且使用较多的一种实现方法——快速union,快速find,基于重量的并查集实现方法。
首先,需要两个数组——parent[] 与weight[] ,parent用来存放该节点的父节点,weight用来存放该节点有多少的子节点,也就是该节点的“重量”。
其次,需要一个整数nums来记录一共有多少个不相连的集合
并查集有三个基本的方法:init() , find() , union() 。init()用来对数组进行初始化,find()用来查找对应节点的根节点,union()用来连接节点。
1,init() 初始化
初始化时,每个节点的父节点都是他自身。每个节点对应的重量均为1,nums为集合中节点的数量,代码如下:
void init() { for(int i = 0 ; i < parent.length ; i ++) { parent[i] = i ; weight[i] = 1 ; } nums = parent,length ; }
2,find()查找对应节点的根节点
find() 在查找根节点时,一直比较节点与父节点是否相同,如果相同则说明该节点为根节点
int find(int p)
{
while(parent[p] != p)
{
p = parent[p] ;
}
return p ;
}
3,union()连接两节点对应的父节点
union()在连接两个节点的父节点时,需要比较两个节点的父节点的“重量”,将重量较大的节点看作为父节点将其合并,每合并一次,对应的nums的数量就会减一。
public static void union(int p,int q,int[] nodes,int[] weight) { int n = find(p,nodes) ; int m = find(q,nodes) ; if(n == m ) return ; //两节点的父节点相同,说明已经连接 if(weight[n] > weight[m]) { nodes[m] = n ; weight[n] += weight[m] ; nums -- ; } else{ nodes[n] = m ; weight[m] += weight[n] ; nums -- ; } }
注意:在创建parent与weight数组时,数组的大小常常为节点个数加一,因为元素的节点常常从1开始,所以,定义的数组从1开始计数更为方便。
下面看两道题目:
It is vitally important to have all the cities connected by highways in a war. If a city is occupied by the enemy, all the highways from/toward that city are closed. We must know immediately if we need to repair any other highways to keep the rest of the cities connected. Given the map of cities which have all the remaining highways marked, you are supposed to tell the number of highways need to be repaired, quickly.
For example, if we have 3 cities and 2 highways connecting city1-city2 and city1-city3. Then if city1 is occupied by the enemy, we must have 1 highway repaired, that is the highway city2-city3.
Input Specification:
Each input file contains one test case. Each case starts with a line containing 3 numbers N (<1000), M and K, which are the total number of cities, the number of remaining highways, and the number of cities to be checked, respectively. Then M lines follow, each describes a highway by 2 integers, which are the numbers of the cities the highway connects. The cities are numbered from 1 to N. Finally there is a line containing K numbers, which represent the cities we concern.
Output Specification:
For each of the K cities, output in a line the number of highways need to be repaired if that city is lost.
Sample Input:
3 2 3
1 2
1 3
1 2 3
Sample Output:
1
0
0
这道题目是对并查集的一种典型应用,可以转化为:每次占领一个城市,并查集连接时便略过该城市,最后查看有几个互不相干的集合n,
便说明了至少还需要n-1条路,代码如下:
import java.util.*; public class BattleOverCities2 { static int[] weight ; static int[] parent; static int nums ; public static void main(String args[]) { Scanner scanner = new Scanner(System.in); int cities = scanner.nextInt(); int roads = scanner.nextInt(); int checked = scanner.nextInt(); int[][] road = new int[roads][2]; for(int i = 0 ; i < roads ; i ++) { road[i][0] = scanner.nextInt(); //road用来记录有多少条道路 road[i][1] = scanner.nextInt(); } int[] check = new int[checked] ; for(int i = 0 ; i < checked ; i ++) { check[i] = scanner.nextInt(); //check[]用来记录被占领的城市 } scanner.close(); for(int i = 0 ; i < checked ; i ++) { init(cities); solve(road,check[i]); System.out.println(nums-2); } } public static void init(int cities) { parent = new int[cities+1] ; nums = cities ; weight = new int[cities+1] ; for(int i = 1 ; i < cities + 1 ; i ++) { parent[i] = i ; weight[i] = 1 ; } } public static int find(int citiy) { while(citiy != parent[citiy]) { citiy = parent[parent[citiy]]; } return citiy ; } public static void union(int[] road ) { int citiy1 = find(road[0]) ; int citiy2 = find(road[1]) ; if(citiy1 == citiy2) return ; if(weight[citiy1] > weight[citiy2]) { parent[citiy2] = citiy1 ; weight[citiy1] += weight[citiy2] ; } else{ parent[citiy1] = citiy2 ; weight[citiy2] += weight[citiy1] ; } nums -- ; } public static void solve(int[][] road , int check) { for(int i = 0 ; i < road.length ; i ++) { if(road[i][0] == check || road[i][1] == check) //当需要连接的城市中有一座被占领,则掠过这条路 continue ; union(road[i]) ; } } }
还有一道题目:
A graph which is connected and acyclic can be considered a tree. The hight of the tree depends on the selected root. Now you are supposed to find the root that results in a highest tree. Such a root is called the deepest root.
Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer N (≤104) which is the number of nodes, and hence the nodes are numbered from 1 to N. Then N−1 lines follow, each describes an edge by given the two adjacent nodes’ numbers.
Output Specification:
For each test case, print each of the deepest roots in a line. If such a root is not unique, print them in increasing order of their numbers. In case that the given graph is not a tree, print Error: K components
where K
is the number of connected components in the graph.
Sample Input 1:
5
1 2
1 3
1 4
2 5
Sample Output 1:
3
4
5
Sample Input 2:
5
1 3
1 4
2 5
3 4
Sample Output 2:
Error: 2 components
这道题目需要求图的生成树的最大深度是多少,首先需要判断图是否都相连,这里用到了并查集,如果互不相连的集合数目大于1,则返回互不相连的
个数,如果为1,则使用dfs找到生成树的最大深度,代码如下:
import java.util.*; public class DeepestRoot { static int nums ; public static void main(String args[]) { Scanner scanner = new Scanner(System.in); nums = scanner.nextInt() ; int[] nodes = new int[nums + 1] ; int[] weight = new int[nums + 1] ; Map<Integer,List<Integer>> map = new HashMap<>() ; for(int i = 1 ; i < nums + 1 ; i ++) { nodes[i] = i ; weight[i] = 1 ; } int t = nums ; for(int i = 1 ; i < t ; i ++) { int p = scanner.nextInt() ; int q = scanner.nextInt() ; union(p,q,nodes,weight); if(!map.containsKey(p)) map.put(p, new ArrayList<Integer>()) ; if(!map.containsKey(q)) map.put(q, new ArrayList<Integer>()) ; map.get(p).add(q) ; map.get(q).add(p) ; } scanner.close(); if(nums != 1) { System.out.println("Error: "+nums+" components"); } else{ int max = 0 ; Map<Integer,List<Integer>> mp = new HashMap<>() ; for(int i = 1 ; i < t + 1 ; i ++) { if(map.get(i).size() == 1) { int c = dfs(map,i,0,new int[t+1],0) ; if(c > max) { mp.put(c, new ArrayList<>()) ; mp.get(c).add(i); max = c ; } else if(c == max) mp.get(max).add(i) ; } } Collections.sort(mp.get(max)); System.out.print(mp.get(max).get(0)); for(int i = 1 ; i < mp.get(max).size() ; i ++) { System.out.println(); System.out.print(mp.get(max).get(i)); } } } public static int dfs(Map<Integer,List<Integer>> map,int q,int count,int[] checked,int max) { checked[q] = 1 ; int p = 0 ; for(int i = 0 ; i < map.get(q).size() ; i ++) { int t = map.get(q).get(i); if(checked[t] == 1) continue ; int l = dfs(map,t,count+1,checked,max) ; if(max < l) max = l ; p ++ ; } if(p == 0) return count ; return max ; } public static int find(int n,int[] parent) { while(parent[n] != n) { n = parent[n] ; } return n ; } public static void union(int p,int q,int[] nodes,int[] weight) { int n = find(p,nodes) ; int m = find(q,nodes) ; if(n == m ) return ; if(weight[n] > weight[m]) { nodes[m] = n ; weight[n] += weight[m] ; nums -- ; } else{ nodes[n] = m ; weight[m] += weight[n] ; nums -- ; } } }