//省份数量
/*
* 有n个城市,其中一些彼此相连,另一些没有相连。如果城市a与城市b直接相连,且城市b与城市c直接相连,
* 那么城市a与城市c间接相连
* 省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
* 给你一个 n * n 的矩阵 isConnected,其中isConnected[i][j]=1表示第i个城市和第j个城市直接相连,
* 而isConnected[i][j]=0表示二者不直接相连
* 返回矩阵中省份(直接或间接相连的是一个省,没有关联的是另一个省)的数量
* */
public class P37 {
public static void main(String[] args) {
System.out.println(getProvince(new int[][]{{1,1,0},{1,1,0},{0,0,1}})); //2
System.out.println(getProvince(new int[][]{{1,0,0},{0,1,0},{0,0,1}})); //3
}
//并查集,并是合并两个树,查是查根节点,直到该合并的都合并了,树和树之间再没办法合并,题目就解开了
//合并的时候,是以树高度大的作为根节点,可以减少维护所有节点指向新节点的次数,并且合并后高度还是不变
//思路:一个一维数组代表城市,不同下标就是不同城市,数组的值是root节点的下标,最后统计这个数组有多少不同的root,就是多少个省
//直接统计不太好,还可以是 数组下标 == 数组值,就是一个省
//并查集
private static int getProvince(int[][] citiesConnected) {
int cities = citiesConnected.length; //初始假设每一个城市都是一个省
int[] head = new int[cities];
int[] level = new int[cities]; //维护层高,用来减少维护head数组的。
// 合并的时候,层高相同时,值都加1
// 层高不同时,小的变为大的值
for (int i=0; i<cities; i++){
head[i] = i; //初始化
level[i] = 1;
}
for(int i=0; i<cities; i++){
for(int j=i+1; j<cities; j++){
if(citiesConnected[i][j] == 1){ //属于同一个省,合并
merge(i, j, head, level);
}
}
}
//统计省份数量
int provinces = 0;
for(int i=0; i<cities; i++){
if(head[i] == i){
provinces++;
}
}
return provinces;
}
//合并方法
static void merge(int x, int y, int[] head, int[] level){
int i = find(x, head); //找x的根节点
int j = find(y, head); //找y的根节点
if(i == j){ //代表同一个树
return;
}
//不是同一个树
if(level[i] <= level[j]){ //合并,矮的合到高的上
head[i] = j; //i的level要++
}else{
head[j] = i; //j的level要++
}
if(level[i] == level[j]){
level[i]++; //比较准确的是把这个判断放在上面,看是i还是j++,不过其实都++并不影响结果,不影响level[i] <= level[j]判断结果
level[j]++;
}
}
//找根节点
//如【0,1,1,2,3】,这样root共有2个,分别是0和1索引,但假如4号索引要找根节点,需要递归,直到找到值等于下标的值
private static int find(int x, int[] head) {
if(head[x] == x){
return x;
}
head[x] = find(head[x], head); //还要维护指针,这样下次就不用再次递归找root(head)了
//[0,1,1,2,1]
return head[x];
}
}
网友评论