並查集深度應用
先來看一道題目:島嶼
現在給你一個m*n矩陣,1表示陸地,0表示海,現在要你統計出這個矩陣上有幾個小島,上下左右聯通的元素屬於一座島。
1 1 0 0 1
1 0 1 1 1
0 1 0 0 0
上例就應該是3。
這題:很明顯是在一個圖上求聯通分量,但是比聯通分量還簡單。
思路:
A.BFS
1.從點(1,1)開始,進行BFS,將遍歷到的點都標記為2,結束一輪BFS,島嶼數量+1
2.遍歷下一個節點,如果已經被標記為2,則直接跳過
複雜度分析:就是遍歷所有為1的節點的複雜度,雖然遍歷矩陣有兩層迴圈,但是實際上的BFS只遍歷了為1的點而已,O(n*m)。
#include<cstdio>
#include<queue>
using namespace std;
int Map[105][105],total,m,n;
typedef struct{
int x,y;
}Point;
void BFS(int x,int y){
queue<Point>que;
Point temp;
int xx,yy;
que.push(Point{x,y});
while(!que.empty()){
temp=que.front();
que.pop();
xx=temp.x;
yy=temp.y;
Map[xx][yy]=2;
if(xx>1&&(Map[xx-1][yy]&1)){
que.push(Point{xx-1,yy});
}
if(xx<n&&(Map[xx+1][yy]&1)){
que.push(Point{xx+1,yy});
}
if(yy>1&&(Map[xx][yy-1]&1)){
que.push(Point{xx,yy-1});
}
if(yy<n&&(Map[xx][yy+1]&1)){
que.push(Point{xx,yy+1});
}
}
}
int main(){
scanf("%d%d",&m,&n);
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
scanf("%d",&Map[i][j]);
}
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(Map[i][j]&1){
BFS(i,j);
total++;
}
}
}
printf("島嶼數量:%d",total);
return 0;
}
B.DFS 這是左神的思路:
實現一個infect(i,j)函式,這個函式會將(i,j)周圍的點感染為2,同時,被感染的點遞迴地去感染他們周圍的點。
#include<cstdio>
using namespace std;
int Map[105][105],total,m,n;
void infect(int x,int y){
if(x<=0||x>m||y<=0||y>n||(Map[x][y]!=1)){
return;
}
Map[x][y]=2;
infect(x-1,y);
infect(x+1,y);
infect(x,y-1);
infect(x,y+1);
}
int main(){
scanf("%d%d",&m,&n);
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
scanf("%d",&Map[i][j]);
}
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(Map[i][j]&1){
infect(i,j);
total++;
}
}
}
printf("島嶼數量:%d",total);
return 0;
}
左神思路分析:其實與BFS差不了多少,遍歷的點的個數也基本相同,相對於BFS來說程式碼精簡,缺陷就是:壓棧和還原環境代價
改:如果現在這個矩陣十分大,使得你只能平行計算,求解。
平行計算的困難:當各個分塊求出來之後,可能存在那種小塊中分離,大塊中合併的聯通塊。
如圖:A和B可能是屬於同一個聯通塊的,但是平行計算中,由於劃分,左邊塊和右邊塊各自有兩個聯通塊,所以給小塊合併帶來了困難。
分析:對於處在邊界的聯通塊要做特殊處理,所以我們需要這兩塊的分界線的邊界資訊。
以上圖為例說明,左塊有兩個,右塊有兩個,當左右合併時,A、B是聯通的,所以可以把他們合併,由於合併一次,就會減少一個聯通塊,所以是三個。
以矩陣為例做分析:
1 1 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 1 0 0 0
0 0 1 0 0 1 1 0 1 1
我們將這個矩陣分兩半,
左部 右部
1 1 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 1 0 0 0
0 0 1 0 0 1 1 0 1 1
我們將各個聯通塊標記出來:
左部 右部
1 1 1 1 1 3 3 3 3 3
0 1 1 1 1 3 3 0 0 0
0 0 0 0 0 0 3 0 0 0
0 0 2 2 2 3 3 0 4 4
所以分開求有4個,我們將兩條邊界線單獨拿出來:
左邊 右邊
1 3
1 3
0 0
2 3
第一行,1和3挨著且不屬於一個集合,所以1和3集合合併,總數量-1變為3
第二行,1和3已經屬於一個集合,往下
第三行,0和0,不連通,往下
第四行:2和3挨著且不屬於一個集合,所以2和3集合合併,總數量-1變為2
結束
所以總的數量為2
相關文章
- 並查集應用並查集
- 並查集的應用2並查集
- 並查集(二)並查集的演算法應用案例上並查集演算法
- 並查集的簡單應用並查集
- 並查集擴充套件應用並查集套件
- 並查集經典應用場景並查集
- 【學習筆記】並查集應用筆記並查集
- 【帶權並查集】理論和應用並查集
- 並查集在實際問題中的應用並查集
- 並查集到帶權並查集並查集
- 查並集
- 【並查集】【帶偏移的並查集】食物鏈並查集
- 並查集(一)並查集的幾種實現並查集
- 並查集(小白)並查集
- [leetcode] 並查集(Ⅱ)LeetCode並查集
- [leetcode] 並查集(Ⅲ)LeetCode並查集
- [leetcode] 並查集(Ⅰ)LeetCode並查集
- 3.1並查集並查集
- 並查集演算法Union-Find的思想、實現以及應用並查集演算法
- 寫模板, 並查集。並查集
- 並查集的使用並查集
- 並查集跳躍並查集
- 各種並查集並查集
- 淺談並查集並查集
- 食物鏈(並查集)並查集
- 並查集(Union Find)並查集
- The Door Problem 並查集並查集
- 並查集練習並查集
- 並查集題目合集並查集
- 並查集java實現並查集Java
- 【轉】種類並查集並查集
- The Suspects-並查集(4)並查集
- 並查集擴充套件並查集套件
- (Day3)並查集並查集
- 並查集演算法並查集演算法
- 並查集-Java實現並查集Java
- 簡單易懂的並查集演算法以及並查集實戰演練並查集演算法
- 專題五 並查集【Kuangbin】並查集