(并查集)lintcode中等434 · 岛屿的个数II

题目

描述
给定 n, m, 分别代表一个二维矩阵的行数和列数, 并给定一个大小为 k 的二元数组A. 初始二维矩阵全0. 二元数组A内的k个元素代表k次操作, 设第i个元素为 (A[i].x, A[i].y), 表示把二维矩阵中下标为A[i].x行A[i].y列的元素由海洋变为岛屿. 问在每次操作之后, 二维矩阵中岛屿的数量. 你需要返回一个大小为k的数组.

设定0表示海洋, 1代表岛屿, 并且上下左右相邻的1为同一个岛屿.

样例

样例 1:

输入: n = 4, m = 5, A = [[1,1],[0,1],[3,3],[3,4]]
输出: [1,1,2,2]
解释: 
0.  00000
    00000
    00000
    00000
1.  00000
    01000
    00000
    00000
2.  01000
    01000
    00000
    00000
3.  01000
    01000
    00000
    00010
4.  01000
    01000
    00000
    00011

样例 2:

输入: n = 3, m = 3, A = [[0,0],[0,1],[2,2],[2,1]]
输出: [1,1,2,2]

分析

看到这道题首先我想到了dfs的做法,就是每次添加之后都dfs判断一次岛屿的数量,但是每次dfs都会做很多重复的工作,所以这里我们处理这种集合的问题,然后频繁的做查询的工作,这里我们用并查集效率是最高的

大体思路:每新添加一座岛屿的时候我们就计数加一,然后判断四个方向是否跟别的岛屿连通,如果连通我们就把他们加入到一个集合中,然后计数减一,因为我们每连通一块岛屿就会少一座岛屿

代码部分

1.并查集部分

这里要跟正常的模板稍作改动,首先给定我们的是一个二维的数组,所以这里节点的编号是一个二维的,也就是一个二维的坐标,然后_size要初始化为0(这里我们要计算每次添加后岛屿的数量,而不是最后的数量)

构造函数
		//行 列 给定数组长度 
		disjoinset(int n,int m,int len)
		{
			_size=0;
			for(int i=0;i<n;i++)
			{
				vector<Point> tmp;
				for(int j=0;j<m;j++)
				{
					Point p(i,j);
					tmp.push_back(p);					
				}
				father.push_back(tmp);
			}
		}
查找函数
		Point find(Point p)
		{
			if(father[p.x][p.y].x==p.x&&father[p.x][p.y].y==p.y)
				return p;
			
			return father[p.x][p.y]=find(father[p.x][p.y]);
		}
连接函数
		void connect(Point a,Point b)
		{
			Point root_a=find(a);
			Point root_b=find(b);
			if(root_a.x==root_b.x&&root_a.y==root_b.y)
				return ;
			father[root_a.x][root_a.y]=root_b;
			_size--;
		} 

2.具体实现(解题部分)

初始化并查集后,我们要建立一个二维数组代表地图,然后初始化一个一维数组存储每次更新完岛屿后的结果

    	vector<vector<int> > _map(n,vector<int>(m,0));

    	disjoinset dis(n,m,operators.size());
    	vector<int> ans;

循环的遍历给定的数组每次改变地图,但是要做一下特判,判断当前位置是否存在岛屿,如果已经存在岛屿就不用后续的操作,如果之前不存在,我们就先计数加一,然后对四个方向进行判断,判断四个方向是否合法,且四个方向是否可以连通,每经历一次岛屿的更新我们记录一次答案

    	for(int i=0;i<operators.size();i++)
    	{
    		int x=operators[i].x;
    		int y=operators[i].y;
			
			if(_map[x][y]!=1)
			{
				_map[x][y]=1;
    			dis._size++;
			}
			
    		for(int j=0;j<4;j++)
    		{
    			int nowx=x+dx[j];
    			int nowy=y+dy[j];
    			if(nowx>=0&&nowx<n&&nowy>=0&&nowy<m&&
				_map[nowx][nowy]==1)
				{
					dis.connect(operators[i],Point(nowx,nowy));
				}
			}
			ans.push_back(dis._size);
		}

完整代码

#include <bits/stdc++.h>
using namespace std;

 struct Point {
	int x;
	int y;
	Point() : x(0), y(0) {}
	Point(int a, int b) : x(a), y(b) {}
};


class disjoinset
{
public:
		vector<vector<Point> > father;
		int _size;
		
		//行 列 给定数组长度 
		disjoinset(int n,int m,int len)
		{
			_size=0;
			for(int i=0;i<n;i++)
			{
				vector<Point> tmp;
				for(int j=0;j<m;j++)
				{
					Point p(i,j);
					tmp.push_back(p);					
				}
				father.push_back(tmp);
			}
		}
		
		Point find(Point p)
		{
			if(father[p.x][p.y].x==p.x&&father[p.x][p.y].y==p.y)
				return p;
			
			return father[p.x][p.y]=find(father[p.x][p.y]);
		}
		
		void connect(Point a,Point b)
		{
			Point root_a=find(a);
			Point root_b=find(b);
			if(root_a.x==root_b.x&&root_a.y==root_b.y)
				return ;
			father[root_a.x][root_a.y]=root_b;
			_size--;
		} 	
};

int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};

class Solution {
public:
    vector<int> numIslands2(int n, int m, vector<Point> &operators) {
    	vector<vector<int> > _map(n,vector<int>(m,0));

    	disjoinset dis(n,m,operators.size());
    	vector<int> ans;
    	for(int i=0;i<operators.size();i++)
    	{
    		int x=operators[i].x;
    		int y=operators[i].y;
			
			if(_map[x][y]!=1)
			{
				_map[x][y]=1;
    			dis._size++;
			}
			
    		for(int j=0;j<4;j++)
    		{
    			int nowx=x+dx[j];
    			int nowy=y+dy[j];
    			if(nowx>=0&&nowx<n&&nowy>=0&&nowy<m&&
				_map[nowx][nowy]==1)
				{
					dis.connect(operators[i],Point(nowx,nowy));
				}
			}
			ans.push_back(dis._size);
		}
		
		return ans;
    }
};

int main (void)
{
	int n=3,m=3;
	vector<Point> A={ {Point(0,0)},{Point(0,1)},{Point(2,2)},{Point(2,1)} };
	Solution s;
	vector<int> ans=s.numIslands2(n,m,A);
	for(int i=0;i<ans.size();i++)
		cout<<ans[i]<<" ";
		
	return 0;
}









总结

这道题如果用dfs的情况,时间复杂度会很高,代码量会比并查集少一些。

并查集就是一个不相交的集合合并到一起,然后查连通块的数量,或者查当前连通块中节点的数量,或者查两个节点是否相通
解决这种问题时并查集的效率是最高的

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
查集(Disjoint Set)是一种数据结构,用于解决集合的合并和查找问题。在Python中可以使用类来实现并查集。引用展示了一个简单的并查集类的代码实现,其中包括了初始化集合、查找集合、合并集合和判断两个元素是否在同一个集合中的方法。另外,引用和展示了对并查集代码的优化,包括路径压缩和按秩合并等技巧,以提高并查集的效率。 在Python中使用并查集可以解决一些实际问题,如求解岛屿个数、朋友圈等。通过将问题转化为集合的合并和查找操作,可以使用并查集来高效地解决这些问题。 所以,如果你需要在Python中实现并查集,可以参考以上的代码实现和优化方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [python 数据结构与算法——并查集](https://blog.csdn.net/itnerd/article/details/103916115)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [并查集Python版](https://blog.csdn.net/XZ2585458279/article/details/127274576)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

White boy&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值