前端必会数据结构与算法系列之算法技巧(十四)

861 阅读9分钟

1. 字典树

字典树,即Trie树,又称单词查找树或键树,是一种树形结构。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。

优点

最大限度地减少无谓的字符串比较,查询效率比哈希表高。

image.png

基本性质

  1. 结点本身不存完整单词;
  2. 从根结点到某一结点,路径上经过的字符连接起来,为该结点对应的字符串:
  3. 每个结点的所有子结点路径代表的字符都不相同。

还可以存储一些额外信息,如图中的数字代表出现频次 image.png

内部实现

每层节点都为26个英文字母

image.png

核心思想

  • Trie树的核心思想是空间换时间。
  • 利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。

image.png

实现 Trie (前缀树)

题解:字典树

单词搜索

题解:单词搜索(回溯)

单词搜索 II

题解: 单词搜索II

2. 并查集(Union-Find)

并查集用来解决组团,配对问题,如朋友圈

基本操作

  • makeSet(s):建立一个新的并查集,其中包含s个单元素集合。
  • unionSet(x, y): 把元素x和元素y所在的集合合并,要求x和y所在的集合不相交,如果相交则不合并。
  • find(x): 找到元素x所在的集合的代表,该操作也可以用于判断两个元素是否位于同一个集合,只要将它们各自的代表比较一下就可以了。

初始化:

image.png

查询、合并

image.png

路径压缩

image.png

剑指 Offer II 116. 朋友圈

题解:并查集

被围绕的区域

3. 布隆过滤器

一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。

优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

hashTable+拉链存储重复元素 image.png

布隆过滤器示意图: image.png

误判:B没有存入,但是查询结果为true image.png

如果都为1,可能存在,如果有一个不为1,则肯定不在。布隆过滤器只作为最外层一个缓存存在,若想确定是否存在,则需要更深层的去数据库查询。

常见应用

  1. 比特币网络
  2. 分布式系统(Map-Reduce)一Hadoop、search engine
  3. Redis缓存
  4. 垃圾邮件、评论等的过滤

推荐文章

布隆过滤器的原理和实现

使用布隆过滤器解决缓存击穿、垃圾邮件识别、集合判重

布隆过滤器 Python 实现示例

4. LRU

最近最少使用原则

  • 大小、替换策略
  • hashTable+Double LinkedList

查询O(1) 修改O(1) 更新O(1)

image.png

LRU 缓存机制

5. 位运算

image.png

image.png

异或:相同为0,不同为1。也可用“不进位加法”来理解。 异或操作的一些特点: x^0 = x
x^1s = ~x // 注意1s = ~0
x^(~x) = 1s
x^x = 0
c = a^b => a^c = b, b^c = a //交换两个数
a^b^c = a^(b^c) = (a^b)^c // associative

指定位置的位运算

  1. 将x最右边的n位清零: x&(~0<<n)
  2. 获取x的第n位值(0或者1) : (x>>n)&1
  3. 获取x的第n位的幂值: x&(1<<(n-1))
  4. 仅将第n位置为1: x|(1<<n)
  5. 仅将第n位置为0: x&(~(1<<n))
  6. 将x最高位至第n位(含)清零: x&((1<<n)-1)
  7. 将第n位至第0位(含)清零: x&(~((1<<(n+1))-1))

实战位运算要点

  • 判断奇偶:
    • x%2==1一>(x&1)==1
    • x%2==0一>(x&1)==0
  • x>>1一>x/2
    • 即: x=x/2 一> x=x>>1;
    • mid=(left+right)/2 一> mid=(left+right)>>1;
      
  • X = X&(X-1)清零最低位的1
  • X&-X=>得到最低位的1
  • X&~X=>0

十进制转2进制

位1的个数

题解: 位1的个数(位运算)

2 的幂

题解: 2的幂

颠倒二进制位

比特位计数

N 皇后

N皇后 II

6. 二分搜索

二分搜索算法的原理和猜数字游戏类似,就是那个有人说“我正想着一个 1~100 的数”的游戏。我们每回应一个数,那个人就会说这个数是高了、低了还是对了。

这个算法要求被搜索的数据结构已排序。以下是该算法遵循的步骤。

  1. 选择数组的中间值。
  2. 如果选中值是待搜索值,那么算法执行完毕(值找到了)。
  3. 如果待搜索值比选中值要小,则返回步骤 1 并在选中值左边的子数组中寻找(较小)。
  4. 如果待搜索值比选中值要大,则返回步骤 1 并在选种值右边的子数组中寻找(较大)。

二分查找前提

  1. 目标函数单调性(单调递增或者递减)
  2. 存在上下界(bounded)
  3. 能够通过索引访问(index accessible)

代码模版

JavaScript版:

let left = 0, right = len(array) - 1 
while (left <= right) {
    let mid = (left + right) >> 1  
    if (array[mid] === target) { 
        /*find the target*/; 
        return 
    } else if (array[mid] < target) {
        left = mid + 1  
    } else {
        right = mid - 1 
    }
}

Java版

int binarySearch(int[] nums, int target) {
    int left = 0;
    int right = nums.length - 1; // 注意
    while(left <= right) {
        int mid = left + (right - left) / 2;
        if(nums[mid] == target)
            return mid;
        else if (nums[mid] < target)
            left = mid + 1; // 注意
        else if (nums[mid] > target)
            right = mid - 1; // 注意
    }
    return -1;
}

image.png

x 的平方根

有效的完全平方数

搜索旋转排序数组

搜索二维矩阵

寻找旋转排序数组中的最小值


爱吃香蕉的珂珂

难度:中等

爱吃香蕉的珂珂(二分法)

去除有序数组重复元素(快慢指针)

删除有序数组中的重复项

题解:删除有序数组中的重复项

删除排序链表中的重复元素

难度:简单

题解:删除排序链表中的重复元素

最长回文子串

难度:中等

最长回文子串(左右指针)

7. 排序

1.比较类排序 通过比较来决定元素间的相对次序,由于其时间复杂度不能突破0(nlogn),因此也称为非线性时间比较类排序。

2.非比较类排序 不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

image.png

初级排序 0(n^2)

  1. 选择排序(Selection Sort)每次找最小值,然后放到待排序数组的起始位置。
  2. 插入排序(Insertion Sort)从前到后逐步构建有序序列;对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
  3. 冒泡排序(Bubble Sort)嵌套循环,每次查看相邻的元素如果逆序,则交换。

特殊排序-O(n)

  1. 计数排序( Counting Sort) 计数排序要求输入的数据必须是有确定范围的整数。将输入的数据值转化为键存储在额外开辟的数组空间中;然后依次把计数大于1的填充回原数组
  2. 桶排序( Bucket sort) 桶排序( Bucket sort的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。
  3. 基数排序( Radix Sort) 基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。

十大经典排序算法

快速排序代码示例

归并排序代码示例

堆排序代码示例

9 种经典排序算法可视化动画

6 分钟看完 15 种排序算法动画展示

数组的相对排序

有效的字母异位词

合并区间

翻转对

8. 高级搜索

初级搜索

  1. 朴素搜索
  2. 优化方式:不重复(fibonacci) 、剪枝(生成括号问题)
  3. 搜索方向:
    • DFS: depth first search深度优先搜索
    • BFS: breadth first search广度优先搜索

优化方向

  • 剪枝 image.png

爬楼梯

括号生成

N 皇后

  • 双向搜索

image.png

image.png

最小基因变化

单词接龙

  • 启发式搜索(A* search)

基于BFS代码:

image.png

A* search: image.png

估价函数

  • 启发式函数: h(n), 它用来评价哪些结点最有希望的是一个我们要找的结点,h(n) 会返回一个非负实数,也可以认为是从结点n的目标结点路径的估计成本。
  • 启发式函数是一种告知搜索方向的方法。它提供了一种明智的方法来猜测哪个邻居结点会导向一个目标。

二进制矩阵中的最短路径

8 puzzles 解法比较

滑动谜题

alphaZero:nikcheerla.github.io/deeplearnin…

DFS: shimo.im/docs/ddgwCc…

BFS: shimo.im/docs/P8TqKH…

9. 字符串算法

转换成小写字母

题解:转换成小写字母

最后一个单词的长度

题解:最后一个单词的长度

宝石与石头

题解:宝石与石头

字符串中的第一个唯一字符

题解:字符串中的第一个唯一字符

字符串转换整数 (atoi)

字符串操作

最长公共前缀

题解:最长公共前缀

反转字符串

仅仅反转字母

翻转字符串里的单词

异位词

有效的字母异位词

字母异位词分组

找到字符串中所有字母异位词

回文串:

验证回文串

验证回文字符串 Ⅱ

最长回文子串

10. 常见问题

1. Sum问题

1. 两数之和

难度:简单

题解:两数之和(字典)

两数之和 II - 输入有序数组

难度:简单

题解:两数之和 II - 输入有序数组(双指针)

三数之和

难度:中等

题解:三数之和(排序+双指针)

四数之和

难度:中等

题解:四数之和

2. 煎饼排序问题

煎饼排序

难度:中等

煎饼排序

3. 前缀和技巧

和为 K 的子数组

难度:中等

题解:和为 K 的子数组(前缀和技巧)

和至少为 K 的最短子数组

4. 其它

Nim 游戏

题解:Nim 游戏

石子游戏

题解:石子游戏

灯泡开关

题解:灯泡开关

在 D 天内送达包裹的能力

被围绕的区域

题解:被围绕的区域

解数独

题解:解数独

有效的数独

题解:有效的数独

剑指 Offer 29. 顺时针打印矩阵