持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第22天,点击查看活动详情
下面是我整理的跟着b站左程云的数据结构与算法学习笔记,欢迎大家一起学习。
岛问题
思路分析:从左往后找到一个非零的点,如下图为第二个1
执行感染(infect)过程,把所有连成1片的1变成2,不断遍历上下左右并将其感染标注成为一片岛
把自身变为2 上下左右递归感染 代码实现
public static int countIslands(int[][] m) {
if (m == null || m[0] == null) {
return 0;
}
int N = m.length;
int M = m[0].length;
int res = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (m[i][j] == 1) {
res++;
infect(m, i, j, N, M);
}
}
}
return res;
}
public static void infect(int[][] m, int i, int j, int N, int M) {
if (i < 0 || i >= N || j < 0 || j >= M || m[i][j] != 1) {
return;
}
m[i][j] = 2;
infect(m, i + 1, j, N, M);
infect(m, i - 1, j, N, M);
infect(m, i, j + 1, N, M);
infect(m, i, j - 1, N, M);
}
整个过程的时间复杂度为O(N* M) 整体遍历阶段 一个位置调用1次 infect过程时,最多调用4次(左右上下)
并查集
支持多个集合快速合并查找的结构 集合支持查找和添加在不同情况下的分析
- 使用hashMap结构保存两个集合的信息,查找时时间复杂度仅为O(1) 但合并时时间复杂度大于O(1)
- 可以直接合并集合,此时时间复杂度为O(1),但查找的时间复杂度大于O(N)
- 并查集的结构可以让查找和合并的时间复杂度都接近O(1)
找到a元素对应的点,往上指的指针直到不能再往上找到的代表元素
若a,b所找出的代表元素不同,则不是同一个集合
合并时,先判断是否为同一个集合,当合并之后,b往上的指针指向a,a往上的元素与b往上的元素相同,为同一集合 少元素的顶部挂在多元素的顶部下面 把两个元素的顶部一改,就能做到快速的查找合并 用往上指的图的方式变成一种高效的并查集 在查询两者是否相同时,将其遍历的父扁平化,即都指向最上层的父,若某一条链过长,可以通过这种方式解决这个瓶颈,下次操作便是直接一步到位找到最上层的父
代码实现
public static class UnionFindSet<V> {
public HashMap<V, Element<V>> elementMap;
public HashMap<Element<V>, Element<V>> fatherMap;
public HashMap<Element<V>, Integer> rankMap;
public UnionFindSet(List<V> list) {
elementMap = new HashMap<>();
fatherMap = new HashMap<>();
rankMap = new HashMap<>();
for (V value : list) {
Element<V> element = new Element<V>(value);
elementMap.put(value, element);
fatherMap.put(element, element);
rankMap.put(element, 1);
}
}
private Element<V> findHead(Element<V> element) {
Stack<Element<V>> path = new Stack<>();
while (element != fatherMap.get(element)) {
path.push(element);
element = fatherMap.get(element);
}
while (!path.isEmpty()) {
fatherMap.put(path.pop(), element);
}
return element;
}
public boolean isSameSet(V a, V b) {
if (elementMap.containsKey(a) && elementMap.containsKey(b)) {
return findHead(elementMap.get(a)) == findHead(elementMap.get(b));
}
return false;
}
public void union(V a, V b) {
if (elementMap.containsKey(a) && elementMap.containsKey(b)) {
Element<V> aF = findHead(elementMap.get(a));
Element<V> bF = findHead(elementMap.get(b));
if (aF != bF) {
Element<V> big = rankMap.get(aF) >= rankMap.get(bF) ? aF : bF;
Element<V> small = big == aF ? bF : aF;
fatherMap.put(small, big);
rankMap.put(big, rankMap.get(aF) + rankMap.get(bF));
rankMap.remove(small);
}
}
}
调用次数超过或接近O(N),平均代价为O(1) 当findhead()调用的情况越高,平均时间复杂度越接近O(1)
如何设计一个并行算法解决这个问题
把二维数组分片并行进行计算
当并行处理时,分割图形时可能存在不一样的结果,
并收集边界感染者的信息并做区分
一开始,分为四个集合A,B,C,D,讨论边界是否相碰 如果相碰,即在相邻位置上,需要将两个岛的集合连通合并 整体岛数量-1;不断判断边界上的点是否相邻合并岛 多CPU模式下可以将图分块,再判断边界是否相邻
KMP算法
还是跟普通一样从左到右匹配遍历返回str2,只不过过程上有加速
前缀与后缀
前缀与后缀相等的最大长度为3
找到前缀与后缀相等(不包括本身)的最大长度 k
设置一个nextArr数组记载最大前缀与后缀相等的长度
如下图中第一个s 前缀最大长度都为3
经典过程 当发现后面有不相同时,str1重新回到i+1,str2重新回到0位置上
而KMP在拥有nextArr后,再碰见这种情况时,比对的位置停在x,而Y跳到跳到他最大前缀与后缀相等的长度,即相应位置的nextArr
后面,让x上的位置直接与他对比
实质是 因为前缀与后缀相同,可以直接跳过一些区域实现加速,将str2直接推到最大前缀与后缀长度之后