查找算法概要

顺序查找

遍历数组中的所有数,寻找等于某个值的数
这是一种十分暴力的算法,没什么好说的。
时间复杂度 O(n) ,空间复杂度为 O(1)
该查找算法对元素的排列没有任何要求,不管是不是有序的。
插入、删除元素的时间复杂度取决于数组的数据结构(顺序表还是链表)。


二分查找

在有序的序列中,每次和序列的中间元素比较,来确定元素的位置。每次比较,如果中间元素不等于被比较元素,就缩小元素所在的范围
二分查找首先要求元素序列是有序的。因此如果元素序列乱序,首先要经过排序,这就要消耗至少 O(nlogn) 的时间。
进行每次二分查找,要么可以返回元素位置,要么就把元素的所在范围缩小一半。因此最快的话一次就可以找到元素,最慢需要 log2n 次比较,时间复杂度为 O(logn)
因为链表不能随机访问(给出任意的元素索引,不能在 O(1) 时间内访问该元素),所以二分查找不适用于链表而适用于顺序表,但是顺序表删除、插入元素会影响到后面的元素(时间复杂度为 O(n) ),所以对于不经常插入、删除的场景下,使用顺序表和二分查找是一个不错的选择。


二叉查找树(BST)

一个二叉树,它中序遍历输出的是一个有序的序列
为了克服二叉查找使用的顺序表插入、删除操作时间复杂度较大的缺点,可使用二叉查找树。
二叉查找树是一个有序的二叉树,把它进行终须遍历输出的是有序的序列。因此,对于二叉查找树的每个节点,左孩子(Left-Children)的值 < 父节点(Parent)的值 < 右孩子的值(Right-Children)或者 左孩子的值 > 父节点的值 > 右孩子的值(先暂时假设数组中所有的元素不相等)。
- 在插入的过程中只要保证小于父节点的元素在左边,大于父节点的在右边(大的在左边小的在右边也行,保持整个树有序即可)就行。
- 查找的时候,把要查找的值和父节点比较:等于就返回父节点,小于就在左边的子树里面查找,大于就在右边的子树查找。
- 删除元素:在删除的同时还要保持二叉树的有序性。所以可以分一下几种情况
1. 被删除的元素是叶子节点:直接删掉就行了
2. 被删除的元素是父节点,只有一个孩子:直接用他的孩子节点覆盖掉这个父节点
3. 被删除的元素是父节点,有两个孩子:把该父节点的直接前驱元素(或者直接后继元素)替换掉这个父节点,同时把前驱元素(或者后继元素)的子树(该前驱、后继元素顶多只有一个子树,自己想一想为什么哈)的根节点放在该前驱元素(或者后继元素)的位置。这个有点复杂,配合图来看比较好理解,图以后再补。

时间复杂度和树的高度h有关。如果该树比较饱满,比较接近于完全二叉树 ,那么树的高度h约为 log2n ;极端情况下,如果这个树每个节点只有一个分支,那么树的高度h就是元素个数n。
因此,对于高度为h的二叉查找树,其查找、插入的时间复杂度都为 O(h) ,即最好为 O(log2n) ,最坏为 O(n) 。删除元素的时间主要消耗在寻找直接前驱元素(或者直接后继)上了,移动二叉树的时间都是常数级的,因此时间复杂度为 O(h) ,即最好为 O(log2n) ,最坏为 O(n)
可以看到,二叉查找树的性能取决于他的树高h。因此,接下来我们介绍一种新的数据结构,平衡二叉树,它在减小树高的方面做了优化。


平衡二叉树(AVL)

平衡二叉树是以BST为基础,对树的高度进行了控制。
首先引入一个概念 平衡因子 :某个节点的左子树减去右子树的深度的差。相对于BST,AVL在每个节点增加一个平衡因子的字段,它所有的节点的平衡因子为0或1或-1(对于平衡二叉树而言)。
这是一个AVL:
平衡二叉树

这是一个不平衡的二叉树:
不平衡的二叉树
A的平衡因子为2(左子树深度3 - 右子树深度1 = 2),因此这个树不平衡。

在讲怎么调整不平衡的二叉树之前,先引入一个概念最小不平衡子树 :某个节点是不平衡的,但是他的左右孩子节点却是平衡的。则以这个节点为根的子树为最小不平衡子树。
最小不平衡子树
可以看出,以46为根节点的子树就是最小不平衡子树。

接下来说说怎么调整AVL:

  • 插入节点:假设在插入节点之前树为AVL,插入之后破坏了平衡性。对于不同类型的树,有不同的调整方案。

    1. LL型或RR型:如下图,中间的树就是LL型的(RR型树正好和LL型树左右对称)。
      LL型二叉树
      首先把B节点转上去,A节点转到B的右孩子的位置,然后把D节点放在A节点的左孩子的位置。这样调整使得插入节点前后,A(插入之后为B)这个子树的高度没有发生变化,因此上层的节点不用调整平衡因子了。RR型的调整方法与LL的类似。

    2. LR型或RL型:如下图,左边的树是LR型的。
      这里写图片描述
      首先对于6这个子树,把6转下去,7转上来,变成中间的树的样子。这个时候树就变成了LL型的,利用之前的调整方法就可以了。同样的,插入前后这个子树的高度不变化(进行了树的平衡的话),不用更新上层节点的平衡因子,只需要更新子树内部的平衡因子。

由上面的方法我们可以看到,插入一个节点,只需要进行常数次的调整,因此对于AVL,插入节点的时间复杂度为 O(1)
如何判断LR、RL、RR、LL型的一个方法(只适用于插入节点):从最小不平衡子树的根节点开始,往插入节点的位置走。如果先走左子树后走右子树,那么就是LR型的。其他类型同理。

  • 删除节点:要调整最小不平衡子树,同样的是首先判断出树的类型,然后将节点转来转去。
    和插入节点不同的是:插入节点如果进行了平衡调整,那么插入入前后最小不平衡子树的高度不发生变化。删除节点之后如果进行了平衡调整,那么删除前后最小不平衡子树的高度可能会发生变化,因此上层祖先节点可能会失衡,需逐层遍历各祖先,并对不平衡的祖先节点进行调整(利用转来转去的方法进行调整)。
    因此,删除节点的时间复杂度为 O(logn)

  • 统一重平衡算法:
    AVL(也可以说BST)的一个最基本的性质就是 它中序遍历出来的序列是有序的,因此可以根据最小不平衡子树的中序遍历结果,删除或者插入相关元素,然后根据修改之后的中序遍历结果来直接生成平衡之后的子树。可以参考《数据结构》邓俊辉著,第三版,P200。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值