查找
顺序查找
没什么说的,从头往后挨个儿匹配
- 时间复杂度$O(n)$
- 查找成功时概率为$1 \over n$平均查找长度为$(n + 1) \over 2$
- 查找不成功时平均查找长度为$n + 1$
- 成功和不成功概率为$1 \over 2$
有序表的查找Binary Search
数据自带顺序,可以使用二分查找
- 时间复杂度$O(log_{2}n)$
- 平均查找长度$log_{2}n + 1$
二叉查找树BST
对于二叉查找树,左子节点的值小于根节点,右子结点的值大于根节点
需要先构建树
[5, 8, 4, 6, 1, 3, 2, 7]
查找成功时的平均查找长度:$$(1 \times 1+ 2\times 2+3\times 2+4 \times 2+5 \times 1) \over 8$$
对于有序数据,可以直接从中间开始构建二叉查找树
hash查找
建立key与数据的映射关系,将key经过hash后存储
一些hash方法
- 直接定址法:直接取key或key的线性函数
- 数字分析法:分析key,取若干位组成hash值
- 平方取中法:key平方后取中间几位
- 折叠法:分割key并叠加
- 除留余数法:
k mod size(hashmap)
解决hash冲突
hash冲突:当$key_1 \neq key_2$时$hash(key_1) = hash(key_2)$
- 开放定址法$hash = hash(key + d_i) mod size(hashmap)$
- 线性探测再散列:$d_i$为线性值$d_i = 1, 2, 3….$
- 二次探测再散列:$d_i = \pm k^2$
- 伪随机探测再散列:$d_i = random$
- 再hash:
hash(hash(key))
- 链地址法:发生冲突时将相关的key组成链表
- 公共溢出区:发生冲突时插入溢出表
Java的HashMap使用了链地址法,在Java8前使用普通的链表,Java8之后,默认如果链表的长度超过8,则转为红黑树,将时间复杂度从$O(n)$变为$O(log n)$
排序
评估标准:时间效率、空间效率、是否稳定
- 时间效率:循环的次数
- 空间效率:局部变量大小
- 稳定:相同的元素不会改变位置
直接插入排序Straight Insertion Sort
- 稳定
- 最坏性能:$O(n^2)$次比较和交换
- 最好性能:数据基本有序$O(n)$次比较,$O(1)$次交换
- 平均性能:$O(n^2)$次比较和交换
- 辅助空间:$O(1)$
折半插入排序Binary Insertion Sort
直接插入排序从后往前找插入位置,而折半插入排序在已排好序的数据中进行折半查找确定插入位置
- 最坏性能:$O(n^2)$次比较和交换
- 最好性能:$O(nlogn)$次比较,$O(1)$次交换
- 平均性能:$O(n^2)$次比较和交换
- 辅助空间:$O(1)$
希尔排序Shell Sort
将待排序列按一定步长分为若干子序列分别进行直接插入排序,再进行全部数据的插入排序
- 不稳定
- 最坏性能:按步长不同性能不同,最常见的为$O(n{log^2}n)$
- 最好性能:$O(nlogn)$
- 平均性能:按步长不同性能不同
- 辅助空间:$O(1)$
冒泡排序Bubble Sort
一次遍历把一个最值放在一端,性能很差,只做学习用
- 稳定
- 最坏性能:$O(n^2)$次比较和交换
- 最好性能:$O(n)$次比较$O(1)$次交换
- 平均性能:$O(n^2)$次比较和交换
- 辅助空间:$O(1)$
快速排序Quick Sort
快速排序是分治算法(divide and conquer),每次循环选定一个数据作为轴pivot,该轴在本次循环内不变,比轴小的数据和轴交换位置,一次循环可以使比轴大的数据都集中到一侧,小的集中到另一侧,再对每侧子集进行快速排序,这样子集有序时,整个数据集就有序了。
轴的选择影响算法效率,如果实现的恰当,可以比归并排序和堆排序快两到三倍
是一些编程语言的默认排序实现
- 高效实现是不稳定的
- 最坏性能:$O(n^2)$很少见
- 最好性能:$O(nlogn)$
- 平均性能:$O(nlogn)$
- 辅助空间:与实现有关
选择排序Selection Sort
每次遍历选择出一个最值加入有序序列
- 是否稳定取决于实现
- 最坏性能:$O(n^2)$比较$O(n)$交换
- 最好性能:$O(n^2)$比较$O(n)$交换
- 平均性能:$O(n^2)$比较$O(n)$交换
- 辅助空间:$O(1)$
堆排序Heap Sort
简单的选择排序有重复比较的情况,堆排序对其进行改进
- 不稳定
- 最坏性能:$O(nlogn)$
- 最好性能:$O(nlogn)$或$O(n)$
- 平均性能:$O(nlogn)$
- 辅助空间:$O(1)$
先构建一棵完全二叉树,再进行调整,让任何非叶子结点的值都大于(或都小于)其左右子节点的值,堆顶一定是最值。
归并排序Merge Sort
另一种分治算法,常用来和快速排序比较
将数据分成子集,对子集进行排序后,再归并成一个有序集合
- 稳定
- 最坏性能:$O(nlogn)$
- 最好性能:$O(nlogn)$变体可以达到$O(n)$
- 平均性能:$O(nlogn)$
- 辅助空间:$O(n)$用链表可以$O(1)$
有迭代法和递归法两种实现
- 迭代法(从下往上):
- 需要声明一个与原集合相同大小的辅助集合
- 每相邻两个元素进行比较排序,形成$ceil(len/2)$个子集
- 归并成每四个元素进行排序,形成$ceil(len/4)$个子集
- 以此类推,直到排序完毕,集合数量为1
- 递归法(从上往下):
- 需要声明一个与原集合相同大小的辅助集合
- 归并前递归的将集合二分,归并中逐个下标的比较两个集合的数据并进行交换
- 将两个集合归并,递归向上组成完整的有序集合