其他分享
首页 > 其他分享> > 力扣547-省份数量

力扣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