题目链接
啊这个题之前有看见过不过那个时候还是很早的时候没什么思路,然后就看了一眼别人的思路,原来就是一个状态空间搜索而已,就用递归写出来了,虽然感觉好像写得没有别人的简洁(见实现2)。用深度优先遍历的时间复杂度是O(MN),空间复杂度是O(MN)(我也不知道多少,如果考虑是在原先的grid上更改的话应该是O(1)?)。当然,更自然的,我们也可以使用广度优先遍历算法,如实现3所示,广度优先遍历的时间复杂度为O(MN),空间复杂度O(min(M,N)),在最欢情况下,整个网格均为陆地,队列的大小可以达到min(M,N)。
最近发现自己不太会做并查集相关的题目,在复习,然后来写一下关于这个题的新做法,即用并查集代替搜索。为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为1,则将其与相邻四个方向上的1在并查集中进行合并。最终岛屿的数量就是并查集中连通分量的数目。关于并查集相关的算法循序渐进的分别有quick-find,quick-union,weighted quick-union,weighted quick-union with path compression。quick-find时间复杂度高,就先不管了。我想首先必须要掌握weighted quick-union,如实现4所示。这样做的话算法中的操作的时间复杂度分别是initialize为N,union为lgN,connected也为lgN。接着,加上更进一步的优化,维护一个额外数组sz,来记录每个根节点对应树的节点数目,在union时比较p和q所在树的节点数目,将节点数量较少的树合并到较大的树上。然后我又在这里写了最终优化完的weighted QU with path compression(如实现3),其中再find函数里使用了路径压缩,并在rank数组上使用了按秩合并,将单次操作的时间复杂度降为了alpha(MN),其中alpha为反阿克曼函数,当自变量在人类可观测范围内(宇宙中的粒子数量)时,函数alpha(x)的值不会超过5,因此也可以看作常数时间复杂度,另外空间复杂度为O(MN)。
最后关于优化quick-union的一点小讨论:关于树的深度,普林斯顿的algorithm课程中提出了一个结论,按照Weighted QU 中的合并方式,任意一个节点的深度不会超过lgN(以2为底),因为每个节点深度加1都需要另一个树的节点数目超过节点所属树的节点数目,因此合并后,该节点所属树的节点数目至少是原来的2倍。从初始所在树的数目为1(只有自身)到最终数目为N(全部节点都属于一棵树),最多只能有lgN次double,深度最多也只能从0增加到lgN。而路径压缩则是在每次查找根节点的时候,可以顺便将子节点的父节点修改为更上一层的节点,如父节点的父节点,这样可以使树的结构变得更扁平。