力扣547-省份数量
作者:互联网
第八十一天 --- 力扣547-省份数量
题目一
力扣:547
思路
经典的图论问题,因为城市间联通等效于边,城市等效于点。
并查集
(有关并查集详细讲解,见我这篇博客力扣648冗余连接,在此不再赘述。)
1、本题其实我第一眼拿来,就是一个并查集问题,因为有很明显的提示—>省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。这里很明显突出了一个省属于一个互相连通的集合,所以维护这种集合问题,最好就用并查集。
2、这个题和经典问题“外星人割网线”,这个题很像,最开始我们维护的并查集内,有N个独立的省份,随着对邻接矩阵的读取,逐渐不同城市间有了连接,他们便属于了一个省份,所以合并一次,ans–,不要重复合并,所以合并之前一定要卡看,他们属不属于一个省份。这样到最后,ans就是省份数量
DFS
1、这个题其实很类似于网格类题目,比如力扣200岛屿数量岛屿数量给的是方格,方格上只能走四个方向,并且他给的只是网格中点的状态,而不是邻接矩阵。
2、这个题就不一样了,本题目任意两个城市之间都有可能有连线,所以从每个点出发,得暴力枚举他所有可能去的点,并且给的是邻接表,表示图的关系。
3、从头遍历每一个城市,如果没走过,说明该城市所在省份树没走过,所以走一遍省份树,到过的城市标记好,每走这么一次dfs,说明一个省份。
4、遍历省份树的时候就和树上的遍历一致,注意他会分出多少可能的枝条就行,因为任意两个城市之间都有可能有连线,所以从每个点出发,得暴力枚举他所有可能去的点,该点得是在邻接矩阵中值为1,代表相连,并且没走过,防止死循环。
5、树形DFS遍历,每找到一个可以到的并且没到过的点,就走下去,那一个枝条完事回溯回来之后,在横向继续找
BFS
这个思路没啥好讲的,就是在按照树形遍历某个省份的时候,不用递归,用队列维护接下来要走的城市,别的和DFS一致。
代码
并查集
注:
1、我们并查集内部是从1到n来维护的,所以传进来的数据得+1
2、我们采用了压缩查询加快速度
3、因为是无向图,所以只需要遍历一半即可.
class query_set { 封装并查集操作
public:
query_set(int n) {
this->n = n;
for (int i = 0; i <= n; i++) { 初始化,每个人都代表一个集合,所以代表元是自己
father[i] = i;
}
}
int find(int item) {
if (item <= 0 || item > n) { 越界处理
return 0;
}
if (father[item] == item) {
return item;
}
father[item] = find(father[item]); 实现了压缩查询
return father[item];
}
void merge(int i, int j) { 合并操作,即相应集合代表元,一个连到另一个人之后
father[find(i)] = find(j);
}
private:
int father[1000]; 并查集
int n;
};
class Solution {
public:
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size();
int ans = n;
query_set qs(n);
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) { 自己连自己没意义,从下一位开始,还能实现遍历一半操作
if (ans == 1) { 极限情况,一个省,剩下不用看了,直接是答案
return ans;
}
if (isConnected[i][j] == 1) {
if (qs.find(i + 1) != qs.find(j + 1)) { 不是一个省的
qs.merge(i + 1, j + 1);
ans--;
}
}
else {
continue;
}
}
}
return ans;
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
DFS
注意:为了防止重复,所以每次扩展新的点的时候,新的点不能是走过的,反之会陷入死循环。
class Solution {
public:
vector<vector<int>> isConnected; 临时所用邻接矩阵
bool is_visit[220] = {}; 访问状态
int ans = 0;
int n = 0; 省份数目
int findCircleNum(vector<vector<int>>& isConnected) {
this->isConnected = isConnected;
n = isConnected.size();
for (int i = 0; i < n; i++) { 每次找没访问过的点进行判断,找到新的省份
if (!is_visit[i]) {
ans++;
dfs(i);
}
}
return ans;
}
void dfs(int i) {
is_visit[i] = true; 新访问的点,置true
for (int j = 0; j < n; j++) { 这里就是树形DFS遍历,每找到一个可以到的并且没到过的点,就走下去,那一个枝条完事回溯回来之后,在横向继续找
if (isConnected[i][j] == 1 && !is_visit[j]) {
dfs(j);
}
}
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
BFS
注意:为了防止重复,所以每次扩展新的点的时候,新的点不能是走过的,反之会陷入死循环。
class Solution {
public:
vector<vector<int>> isConnected; 临时所用邻接矩阵
bool is_visit[220] = {}; 访问状态
int ans = 0;
int n = 0; 省份数目
int findCircleNum(vector<vector<int>>& isConnected) {
this->isConnected = isConnected;
n = isConnected.size();
for (int i = 0; i < n; i++) { 每次找没访问过的点进行判断,找到新的省份
if (!is_visit[i]) {
ans++;
bfs(i);
}
}
return ans;
}
void bfs(int i) {
queue<int> q;
q.push(i);
while (!q.empty()) { 用队列维护
int tmp = q.front();
q.pop();
is_visit[tmp] = true;
for (int j = 0; j < n; j++) { 这个就是一个类似树形的遍历过程,每层都横向的找到所有可能的点,然后存入队列
if (isConnected[tmp][j] == 1 && !is_visit[j]) {
q.push(j);
}
}
}
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
标签:遍历,int,查集,isConnected,力扣,547,ans,省份 来源: https://blog.csdn.net/qq_45678698/article/details/120752468