1. 岛问题(并查集)
方法一:普通版
思路:
利用infect函数把一片1统一变成2,本质上是记录访问过的1
//"感染"函数
/*
* vec:必须传引用,因为修改的值需要保存,并且引用的是原容器的副本,所以不会修改原先内容
* i:行
* j:列
* m:行界限
* n:列界限
*/
void infect(vector<vector<int>>& vec, int i, int j, int m, int n) {
if (i < 0 || i >= m || j < 0 || j >= m || vec[i][j] != 1)return;
//vec[i][j]=1,修改成2
vec[i][j] = 2;
//由于for循环遍历的方向是从左到右,从上到下,因此每个1必定是从它的左边或上边抵达,因此只需向右和向下走
infect(vec, i, j + 1, m, n);
infect(vec, i + 1, j, m, n);
}
递归时间复杂度: O(N*M)
int countIslands(vector<vector<int>>vec) {//不传引用避免修改原容器里的内容
int m = vec.size();//行
int n = vec[0].size();//列
int res = 0;//岛的数量
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (vec[i][j] == 1) {
//到达1处才开始感染,每调用一次感染岛数+1
res++;
infect(vec, i, j, m, n);
}
}
}
return res;
}
并查集:向上指的结构
//样本进来包一层,叫做元素
template<typename V>
class Element{
public:
V value;
Element(V value) {
this->value = value;
}
};
//定义并查集结构
template<typename V>
class UnionFindSet {
public:
//key元素对应value元素"圈"
map<V, Element<V>>elementMap;
//value元素是key元素的父
map<Element<V>, Element<V>>fatherMap;
//key元素是某个集合的代表元素,value是该集合的大小
map<Element<V>, int>sizeMap;
//并查集在构建时要求用户把样本都传过来
UnionFindSet(list<V>ls) {
for (V value : list) {
Element<V>element = new Element<V>(value);
elementMap[value] = element;
fatherMap[element] = element;
sizeMap[element] = 1;
}
}
Element<V>findHead(Element<V>element) {
stack<Element<V>>path;
while (element != fatherMap[element]) {
path.push(element);
element = fatherMap[element];
}
//把结构变成扁平的,降低高度
//不用考虑每个元素集合size的变化,因为改变的元素都不是代表元素
while (!path.isEmpty()) {
fatherMap[path.top] = element;
path.pop();
}
return element;
}
bool isSameSet(V a, V b) {
if (elementMap.find(a) != elementMap.end() && elementMap.find(b) != elementMap.end()) {
return findHead(elementMap[a]) == findHead(elementMap[b]);
}
return false;
}
void union(V a, V b) {
if (elementMap.find(a) != elementMap.end() && elementMap.find(b) != elementMap.end()) {
Element<V>aF = findHead(elementMap[a]);
Element<V>bF = findHead(elementMap[b]);
if (aF != bF) {
Element<V>big = sizeMap[aF] > sizeMap[bF] ? aF : bF;
Element<V>small = big == aF ? bF : aF;
fatherMap[small] = big;
sizeMap[big] += sizeMap[small];
sizeMap.erase(small);
}
}
}
};
方法二:并行算法
问题1:切开图分配给不同的CPU之后,可能会把原本连通的切断。
解决1:记录切割边边界点的感染源。不同的感染源感染到的点分配在不同集合中,最后通过能否合并集合来算出实际岛数