哈希表
哈希表是一种key-value的存储结构,本质上是一个线性表的结构,其好处是可以根据键值快速索引到value,其号称是o(1)的操作。
在常规的线性表结构(如数组、链表等)中,记录在结构中的相对位置时随机的,在结构中查找记录时需进行一系列和关键字的比较。这类查找方法建立在“比较”的基础上,效率依赖于查找过程中的比较次数。 希望的理想情况是不经过任何比较,一次存取便能得到查找记录,那就必须在记录的存储位置和它的关键字之间建立一个对应关系 f。例如记录的关键字为 K,那该条记录必定存储在 f(K) 的位置上。因此在查找时只需要根据对应关系 f 就能找到给定 K 的位置。我们称这个对应关系 f 为哈希(Hash)函数,按这个思想建立的表为哈希表。
举个例子:
例如超市中的每件商品都有其对应的条码,售卖时通过条码(key)查询到具体的商品信息(value), 分别以线性表、哈希表存建立数据库,数据查询过程示例如下:
我们以取余建立哈希函数:
线性表查询时间复杂度取决于比较次数,哈希表查询时间复杂度为 1。
哈希函数的设计
计算出来的地址分布均匀,即对任一关键字K,H(K)对应不同地址的概率相等,目的是尽可能减少冲突。
哈希表地址冲突的处理
哈希函数是一个映射关系,可以自由灵活设置,只要使任何关键字由此函数得到的哈希值都落在表长允许的范围内即可;
对不同的关键字可能得到同一哈希地址,即 key1 ≠ key2, 而 f(key1)=f(key2),这种现象称为哈希冲突。一般情况下只能尽量减少冲突(例如建立合适大小的表、选择合适的哈希函数等),而不能完全避免。我们可以通过链地址法处理冲突。
通过链地址法处理冲突的哈希表示例
内存优化
在二分查找中,我们经常使用kd-tree,其存储结构一般是由链表完成的,链表的存储并不是一段连续的空间,每一个节点的创建都需要申请内存,而且所占用的内存并不仅仅是待使用的内存,还有记录内存的信息。
预先分配内存不仅可以节省空间,而且连续的数据块更方便读取,所以可以加速读取。
KD树预分配节点内存加速数据存取。
积分图
积分图可以用于快速的邻域求和。
积分图的目的是累计求和一定范围内的像素值(比如矩形、正方形、或者圆形);
积分图能够加速求和的本质在于已经计算过的区域不用再次计算;
积分图的初始化:
首先建立一个数组 A 作为积分图像,其宽高与原图像相等. 然后对这个数组赋值,每个点存储的是该点与图像原点所构成的矩形中所有像素的和:
其中 I(x,y) 表示图像 (x,y) 位置的像素值。积分图像可以采用增量的方式计算:
定义了积分图的概念,就可以很方便的计算任意区域内的像素和,如下图所示:
点1的积分 SAT1=Sum(Ra),
点2的积分 SAT2=Sum(Ra)+Sum(Rb),
点3的积分 SAT3=Sum(Ra)+Sum(Rc),
点4的积分 SAT4=Sum(Ra)+Sum(Rb)+Sum(Rc)+Sum(Rd)
那么为了计算某个矩形像素和,比如区域 Rd 内所有点的像素值之和(积分)可以表示为:
Sum(Rd)=SAT1+SAT4−SAT2−SAT3(3)
所以无论矩形的尺寸大小,只需查找积分图像 4 次就可以快速计算任意矩形内像素值的和, 即算法复杂度为 O(4)。