vlambda博客
学习文章列表

二分查找法和平衡二叉树

本文部分内容来自《图解算法》和《MySQL技术内幕 InnoDB存储引擎》

在研究数据库索引B+树的数据结构之前,需要对二分查找、平衡二叉树等有一个基础的了解,才能更加清楚B+树索引的工作方式。

一、二分查找法

1、概述

        二分查找(binary search)法也叫做折半查找,输入一个有序的元素列表,如果要查找的元素包含在列表中,二分查找返回其位置,否则返回null。在查找过程中采用跳跃式方法查找,先以有序数列的中点位置为比较对象,如果要找的元素小于该中点值,则查找序列缩小为左半部分,否则缩小为右半部分。所以,二分查找法每进行一次比较,查找区间均缩小一半。

2、示例

        下面通过一个示例来说明二分查找的工作原理。在1到100之间随便取一个数字:

        假设另外一个人的目标是以最少的次数猜到这个数字,每次猜后,都会得到大了、小了或对了的结果反馈。假设他从1开始猜,会一直往上面猜,第一次猜1,第二次猜2,一直递增,每次都只能排除一个数字。如果数字为100,需要猜100次才能猜到。这种查找方式为简单查找。

         二分查找为:假设设置数字为57,第一次猜测数字为50,得到小了的结果,故排除了1~50之间的所有数字。第二次会猜测数字为75,得到大了的结果,剩余的50个数字又排除了一半。**使用二分查找时,猜测的是中间的数字,故每次都将剩余的数字排除一半。**接下来,会猜测为63(50和75之间的数字),一直到猜到57这个数字。100个元素,不论设置哪个元素,都可以在7次以内猜到。

        一般而言,对于包含n个元素的列表,二分查找最多需要log以2为底n的对数步,而简单查找最多需要n步。

3、代码实现

# 该代码来源于图解算法中示例,这里使用的是python2版本,所以求中间位置时直接使用/计算即可,python3可以使用//算法得到一个整数,如果得到的值是一个小数,python3会直接得到这个浮点值
def binary_search(list, item):
low = 0
high = len(list) - 1 # low和high参数主要用于追踪每次需要查找的范围

while low <= high: # 只要范围没缩小到只包含一个元素,就执行下面的操作
mid = (low + high)/2 # 得到中间的元素的位置
guess = list[mid] # 得到中间元素的值
if guess == item: # 找到了对应元素
return mid # 返回对应元素所在位置
if guess > item: # 猜的数字大了
high = mid -1 # 下一轮最大的数字所在位置为mid - 1
else: # 猜的数字小了
low = mid + 1 # 下一轮最小数字所在位置为mid + 1
return None # 没有指定元素,就返回None

二、二叉查找树

二分查找法和平衡二叉树

        上图是一个二叉查找树,其中的数字代表了每个节点的键值,在二叉查找树中,左子树(节点的左分叉)的键值总是小于根的键值,右子树的键值总是大于根的键值。因此可以通过中序遍历得到键值的排序输出:2、3、5、6、7、8

二叉树四种主要的遍历思想为:
前序遍历:根结点 ---> 左子树 ---> 右子树
中序遍历:左子树---> 根结点 ---> 右子树
后序遍历:左子树 ---> 右子树 ---> 根结点
层次遍历:只需按层次遍历即可
例如:该二叉树的前序遍历为 6、3、2、5、7、8

        对上面这个二叉树进行查找,例如想要找到键值为5的记录,先找到根,根键值为6,比5大,所以查找6的左子树,找到3;而5比3大,查找3的右子树,找到5。一共找了3次,按照中序遍历的顺序查找也需要3次。同样的,若想要找键值为8的记录,使用二叉查找树也需要3次,但顺序查找就需要6次。

三、平衡二叉树

        二叉查找树可以任意构造,但不同的二叉树会导致查找效率存在很大不同。因此若想最大性能构造一个二叉查找树,需要它是平衡的,也就是平衡二叉树(AVL树)。

        平衡二叉树指:在符合二叉查找树的基础上,不许保证任何节点的两个子树的高度最大差为1,可以看出,上面二叉查找树中的图也是一个平衡二叉树。平衡二叉树的性能是比较高的,但不是最高的,最好性能需要建立一个最优二叉树,但最优二叉树的创建和维护成本都比较高,所以一般平衡二叉树足够满足查询性能要求。

        平衡二叉树的查询性能很高,但维护一个平衡二叉树的成本非常大,通常来说,插入一个新的节点后,需要通过1次或多次左旋和右旋来得到新的平衡二叉树。下面通过插入新的键值9来表示更新过程: