并查集结构
在一个集合中有N个元素,每一个元素都可以作为单独的一个集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。
并查集具体的做法,查找两个元素是否属于同一个集合,如果两个不是同一个集合就涉及到两个元素的合并操作。
package course15;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
/**
* 并查集
*/
public class Code06_UnionFind {
//节点
public static class Node<V>{
V value;
public Node(V value) {
this.value = value;
}
}
public static class UnionSet<V>{
/*包上一层*/
public HashMap<V,Node<V>> nodes;
/*找父亲的一个元素*/
public HashMap<Node<V>,Node<V>> parents;
/*这个元素集合下有多少个元素*/
public HashMap<Node<V>,Integer> sizeMap;
/*初始化操作*/
public UnionSet(List<V> value) {
nodes = new HashMap<>();
parents = new HashMap<>();
sizeMap = new HashMap<>();
for (V cur : value){
Node<V> node = new Node<>(cur);
nodes.put(cur,node);
parents.put(node,node);
sizeMap.put(node,1);
}
}
//给你一个节点,请你往上爬到不能在爬以后返回
public Node<V> findFather(Node<V> cur){
Stack<Node<V>> path = new Stack<>();
while(cur!=parents.get(cur)){
path.push(cur);
cur = parents.get(cur);
}
//这个操作是为了避免链式过长,这个是将他变的扁平化
while(!path.isEmpty()){
parents.put(path.pop(),cur);
}
return cur;
}
/*判断两个节点是否是属于同一个集合*/
public boolean isSameSet(V a,V b){
return findFather(nodes.get(a)) == findFather(nodes.get(b));
}
/*两个节点的合并操作*/
public void union(V a,V b){
Node<V> aHead = findFather(nodes.get(a));
Node<V> bHead = findFather(nodes.get(b));
if (aHead != bHead){
//查询 a,b集合的大小
int aSetSize = sizeMap.get(aHead);
int bSetSize = sizeMap.get(bHead);
//大小集合的重定向
Node<V> big = aSetSize >= bSetSize ? aHead:bHead;
Node<V> small = big == aHead?bHead:aHead;
parents.put(small,big);
sizeMap.put(big,aSetSize+bSetSize);
sizeMap.remove(small);
}
}
}
}
练习题目
1、朋友圈
package course15;
public class Code08_FriendCircle {
public static void main(String[] args) {
int[][] nums = {{1,0,1,0,1},{0,1,0,1,0},{1,0,1,0,0},{0,1,0,1,0},{1,0,0,0,1}};
int circleNum = findCircleNum(nums);
System.out.println(circleNum);
}
public static int findCircleNum(int[][] M){
int N = M.length;
UnionFind unionFind = new UnionFind(N);
for (int i = 0;i<M.length;i++){
for (int j = i+1;j<M.length;j++){
if (M[i][j] == 1){
unionFind.union(i,j);
}
}
}
return unionFind.getNumber();
}
public static class UnionFind{
//parent[i] = k , i的父亲是k
private int[] parent;
//parent[i] = k , 如果i是代表节点,size[i]才有意义,否则无意义
//i所在的集合大小是多少,每一个集合下有多少的元素
private int[] size;
private int[] help;
//一共有多少个集合
private int sets;
public UnionFind(int N) {
parent = new int[N];
size = new int[N];
help = new int[N];
//集合的数量
sets = N;
for (int i=0;i<N;i++){
parent[i] = i;
size[i] = 1;
}
}
//向上找
private int find(int i){
int hi = 0;
while(i != parent[i]){
help[hi++] = i;
i = parent[i];
}
for (hi--;hi>=0;hi--){
parent[help[hi]] = i;
}
return i;
}
//合并
public void union(int i,int j){
int f1 = find(i);
int f2 = find(j);
if (f1!=f2){
if (size[f1] >= size[f2]){
size[f1] += size[f2];
parent[f2] = f1;
}else{
size[f2] += size[f1];
parent[f1] = f2;
}
sets--;
}
}
public int getNumber(){
return sets;
}
}
}
2、岛屿问题
package course15;
public class Code09_LandsNum {
public static void main(String[] args) {
char[][] nums = {
{'1','0','0','0','1','1','0'},
{'1','1','1','0','0','1','0'},
{'0','0','0','0','1','1','0'},
{'0','1','0','0','0','1','0'},
{'1','1','0','0','0','0','0'},
{'1','0','0','0','1','1','0'},};
System.out.println(islandNum3(nums));
}
public static int islandNum3(char[][] board){
int islands = 0;
int N = board.length;
for (int i=0;i<N;i++){
for (int j=0;j<board[0].length;j++){
if (board[i][j] == '1'){
islands++;
infect(board,i,j);
}
}
}
return islands;
}
private static void infect(char[][] board, int i, int j) {
if (i<0 || i == board.length || j<0 || j == board[0].length || board[i][j]!='1'){
return;
}
board[i][j] = '2';
infect(board,i-1,j);
infect(board,i+1,j);
infect(board,i,j+1);
infect(board,i,j-1);
}
}
3、相同的用户
package course15;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
public class Code07_isSamePersons {
private static class User{
String idCard;
String bid;
String gitHubId;
public User() {}
public User(String idCard, String bid, String gitHubId) {
this.idCard = idCard;
this.bid = bid;
this.gitHubId = gitHubId;
}
}
public static int mergeUsers(List<User> users){
UnionSet unionSet = new UnionSet(users);
HashMap<String,User> mapIdCard = new HashMap<>();
HashMap<String,User> mapBid = new HashMap<>();
HashMap<String,User> mapGitHubId = new HashMap<>();
for (User user : users){
if (mapIdCard.containsKey(user.idCard)){
unionSet.union(user.idCard,mapIdCard.get(user.idCard));
}else{
mapIdCard.put(user.idCard,user);
}
if (mapBid.containsKey(user.bid)){
unionSet.union(user.bid,mapIdCard.get(user.bid));
}else{
mapIdCard.put(user.bid,user);
}
if (mapGitHubId.containsKey(user.gitHubId)){
unionSet.union(user.gitHubId,mapIdCard.get(user.gitHubId));
}else{
mapIdCard.put(user.gitHubId,user);
}
}
return unionSet.getSetNum();
}
public static class Node<V>{
V value;
public Node(V value) {
this.value = value;
}
}
public static class UnionSet<V>{
/*包上一层*/
public HashMap<V, Node<V>> nodes;
/*找父亲的一个元素*/
public HashMap<Node<V>, Node<V>> parents;
public HashMap<Node<V>,Integer> sizeMap;
public UnionSet(List<V> value) {
nodes = new HashMap<>();
parents = new HashMap<>();
sizeMap = new HashMap<>();
for (V cur : value){
Node<V> node = new Node<>(cur);
nodes.put(cur,node);
parents.put(node,node);
sizeMap.put(node,1);
}
}
//给你一个节点,请你往上爬到不能在爬以后返回
public Node<V> findFather(Node<V> cur){
Stack<Node<V>> path = new Stack<>();
while(cur!=parents.get(cur)){
path.push(cur);
cur = parents.get(cur);
}
while(!path.isEmpty()){
parents.put(path.pop(),cur);
}
return cur;
}
public boolean isSameSet(V a,V b){
return findFather(nodes.get(a)) == findFather(nodes.get(b));
}
public void union(V a,V b){
Node<V> aHead = findFather(nodes.get(a));
Node<V> bHead = findFather(nodes.get(b));
if (aHead != bHead){
//查询 a,b集合的大小
int aSetSize = sizeMap.get(aHead);
int bSetSize = sizeMap.get(bHead);
//大小集合的重定向
Node<V> big = aSetSize >= bSetSize ? aHead:bHead;
Node<V> small = big == aHead?bHead:aHead;
parents.put(small,big);
sizeMap.put(big,aSetSize+bSetSize);
sizeMap.remove(small);
}
}
public int getSetNum() {
return sizeMap.size();
}
}
}