从二分查找到二叉树到堆

182 阅读4分钟

二分查找

定义

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列.时间复杂度 O(logn)

实现思路

递归 - 代码逻辑更加简洁

const binarySearch = (arr,target,startIndex,endIndex) => {
   const length = arr.length;
   if(length === 0 ) return -1;
   // 开始和结束的范围
   if(startIndex == null) startIndex = 0;
   if(endIndex == null) endIndex = length -1;
   // 如果 start 和 end 相遇,则结束
   if(startIndex > endIndex) return - 1;
   // 中间位置
   const midIndex = Math.floor((startIndex + endIndex) / 2);
   const midValue = arr[midIndex];
   if(target < midValue){
       // 目标值较小,则继续在左侧查找
       return binarySearh(arr, target, startIndex,midIndex - 1)
   }else if(target > midValue){
       // 目标值较大,则继续在右侧查找
       return binarySearh(arr, target, midIndex + 1,endIndex)
   }else{
       // 相等,返回
       return midIndex
   }
}

// // // 功能测试
// const arr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]
// const target = 40
// // console.info(binarySearch2(arr, target))

循环 - 性能更好(就调用一次函数,而递归需要调用很多次函数,创建函数作用域会消耗时间)

   const binarySearch = (arr,targetr)=>{
       const length = arr.length;
       if(length === 0) retrun -1;
       let startIndex = 0;
       let endIndex = length -1;
       while(starIndex <=endIndex){
        const midIndex = Math.floor((startIndex+endIndex)/2);
        const midValue = arr[midIndex];
             if(target<midValue){
                    endIndex = midIndex -1
                }else if(target>midValue){
                    startIndex = midIndex +1
                }else{
                    return midIndex
                }
       }
       return -1
   } 

二叉树

定义

树是常见的数据结构,如 DOM 树,是一种多叉树。二叉树(binary tree)是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。

为什么重要?

  • 二叉树,可以充分利用二分法
  • 二叉树可以同时规避数字和链表的缺点

二叉树的遍历

  • 前序遍历:root -> left -> right
  • 中序遍历:left -> root -> right
  • 后序遍历:left -> right -> root
    interface ITreeNode {
        value: number
        left: ITreeNode | null
        right: ITreeNode | null
    }
    const arr: number[] = []
    const preOrderTraverse = (node:ITreeNode | null):void =>{
        if(node === null) return;
        arr.push(node.value);
        preOrderTraverse(node.left);
        preOrderTraverse(node.right);
    }

二叉搜索树 BST

有序结构

  • 数组:查找易,增删难
  • 链表:增删易,查找难

将两者优点结合起来 —— 二叉搜索树 BST :查找易,增删易 —— 可使用二分算法

特性

1.子树上所有结点的值均小于或等于它的根结点的值。

2.子树上所有结点的值均大于或等于它的根结点的值。

3.左、右子树也分别为二叉排序树。

1661485411298.png

寻找 BST 里的第 K 小值

根据 BST 的特点,中序遍历的结果,正好是按照从小到大排序的结果。 所以,中序遍历,求数组的 arr[k] 即可。

BBST (Balance Binary Search Tree) 平衡二叉搜索树

二叉搜索树 BST ,如果左右不平衡,也无法做到最优

极端情况下,它就成了链表

1661740100105.png

适度平衡的BST称为BBST

  • 树高度 h 约等于 logn
  • 查找、增删,时间复杂度都等于 O(logn)

红黑树

一种自动平衡的二叉树

  • 节点分 红/黑 两种颜色,通过颜色转换来维持树的平衡
  • 相比于普通平衡二叉树,它维持平衡的效率更高

红黑树的特性(规则)如下:

1.结点是红色或黑色。

2.根结点是黑色。

3.每个叶子结点都是黑色的空结点(NIL结点)。

4.每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)

5.从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点。

1661740945488.png mp.weixin.qq.com/s/X3zYwQXxq…

AVL (Adelson-Velsky-Landis Tree) 自适应平衡二叉搜索树

在AVL树当中,我们通过“平衡因子”来判断一颗二叉树是否符合高度平衡。

到底什么是AVL树的平衡因子呢?

对于AVL树的每一个结点,平衡因子是它的左子树高度和右子树高度的差值。只有当二叉树所有结点的平衡因子都是-1, 0, 1这三个值的时候,这颗二叉树才是一颗合格的AVL树

举个例子,下图就是一颗典型的AVL树,每个节点旁边都标注了平衡因子:

1661741828240.png

其中结点4的左子树高度是1,右子树不存在,所以该结点的平衡因子是1-0=1。

结点7的左子树不存在,右子树高度是1,所以平衡因子是0-1=-1。

所有的叶子结点,不存在左右子树,所以平衡因子都是0。

B 树

物理上是多叉树,但逻辑上是一个 BST 。用于高效 I/O ,如关系型数据库就用 B 树来组织数据结构。

哈夫曼树

夫曼树(Huffman Tree)是在叶子结点和权重确定的情况下,带权路径长度最小的二叉树,也被称为最优二叉树。

mp.weixin.qq.com/s/dX-8zmSem… mp.weixin.qq.com/s/3uCQj0-WB…

  • 完全二叉树
  • 最大堆:父节点>=子节点
  • 最小堆:父节点<=子节点

1661755115197.png

1661755142035.jpg

堆,虽然逻辑上是二叉树,但实际上它使用数组来存储的

1661755366461.png

1661755398592.jpg

堆对比BST

  • 查询比 BST 慢
  • 增删比 BST 快,维持平衡也更快
  • 但整体复杂度都是 O(logn) 级别,即树的高度

但结合堆的应用场景

  • 一般使用内存地址(栈中保存了)来查询,不会直接从根节点搜索
  • 堆的物理结构是数组,所以查询复杂度就是 O(1)

总结

  • 物理结构是数组(空间更小),逻辑结构是二叉树(操作更快)
  • 适用于“堆栈”结构