第93题 求一个二叉搜索树的第k小值

二叉树

  • 二叉树是一棵树
  • 二叉树每个节点最多只能有两个子节点
  • 树节点的数据结构{value,left?,right?}
  • 二叉树的遍历
    • 前序遍历:root => left => right
    • 中序遍历:left => root => right
    • 后序遍历:left => right => root
  • 二叉搜索树BST(Binary Search Tree)的特点
    • left(包含其后代)value <= root value
    • right(包含其后代)value >= root value
    • 二叉搜索树的价值可以让我们使用二分法 快速查找

思路分析

  • 二叉搜索树中序遍历 ,即从小到大的排序
  • 找到排序后的第k值即可
    /**
     * @description 二叉搜索树
     */
    
    // 数据结构
    interface ITreeNode {
      value: number
      left: ITreeNode | null
      right: ITreeNode | null
    }
    
    const arr = []
    
    /**
     * 二叉树前序遍历
     * @param node:ITreeNode tree node
     */
    function preOrderTraverse(node) {
      if (node == null) return
      // console.log(node.value)
      arr.push(node.value)
      preOrderTraverse(node.left)
      preOrderTraverse(node.right)
    }
    
    /**
     * 二叉树中序遍历
     * @param node:ITreeNode tree node
     */
    function inOrderTraverse(node) {
      if (node == null) return
      inOrderTraverse(node.left)
      // console.log(node.value)
      arr.push(node.value)
      inOrderTraverse(node.right)
    }
    
    /**
     * 二叉树后序遍历
     * @param node:ITreeNode tree node
     */
    function postOrderTraverse(node) {
      if (node == null) return
      postOrderTraverse(node.left)
      postOrderTraverse(node.right)
      // console.log(node.value)
      arr.push(node.value)
    }
    
    /**
     * 寻找 BST 里的第 K 小值
     * @param node tree node:ITreeNode
     * @param k:number 第几个值
     */
    function getKthValue(node, k) {
      inOrderTraverse(node) // 中序遍历
      // arr => [2, 3, 4, 5, 6, 7, 8]
      // k - 1从0开始
      return arr[k - 1] || null
    }

  • 前序遍历(根左右):5324768
  • 中序遍历(左根右):2345678
  • 后序遍历(左右根):2436875
    // 测试
    const bst = {
        value: 5,
        left: {
            value: 3,
            left: {
                value: 2,
                left: null,
                right: null
            },
            right: {
                value: 4,
                left: null,
                right: null,
            }
        },
        right: {
            value: 7,
            left: {
                value: 6,
                left: null,
                right: null
            },
            right: {
                value: 8,
                left: null,
                right: null
            }
        }
    }
    
    // preOrderTraverse(bst)
    // inOrderTraverse(bst)
    // postOrderTraverse(bst)
    
    console.log(getKthValue(bst, 3)) // 4

拓展:为什么二叉树很重要,而不是三叉树四叉树

因为二叉树可以进行二分法快速查找

  • 数组:查找快,时间复杂度O(1),增删慢,时间复杂度O(n)
  • 链表:查找慢,时间复杂度O(n),增删快,时间复杂度O(1)
  • 二叉搜索树BST查找快增删快,可以弥补数组、链表的缺点

1.平衡二叉树

  • BST如果不平衡,那就成链表了
  • 所以要尽量平衡:平衡二叉搜索树 BBST
  • BBST增删查,时间复杂度都是O(logn),即树的高度(n树的节点数)

2.红黑树

  • 一种自平衡二叉树
  • 分为红/黑两种颜色,通过颜色转化来维持树的平衡
  • 相对于普通平衡二叉树,它维持平衡的效率更高

3.B树

  • B树是二叉树的一个变种,目的是为了高效维持平衡,高效去计算
  • 物理上是多叉树,逻辑上是二叉树
  • 一般用于高效I/O,关系型数据库(MySQL)常用B树来组织数据

4.小结

  • 数组链表各有各的缺点
  • 特点的二叉树(平衡二叉树BBST)可以让整体效果最优
  • 各种高级二叉树,继续优化,满足不同场景

拓展:堆有什么特点,和二叉树有什么关系

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

逻辑结构vs物理结构

  • 堆,逻辑结构是一颗二叉树,物理结构是一个数组
  • 数组:适合连续存储 + 节省空间

堆 vs BST

  • 查询比BST慢,BST规则比较严格可以使用二分
  • 增删比较BST快,维持平衡更快,因为规则比较简单
  • 但整体的时间复杂度都在O(logn)级别,即树的高度(n树的节点数)

堆的使用场景

  • 堆的数据都是在栈中引用的,不需要从root遍历
  • 堆恰巧是数组形式,根据栈的地址,可用O(1)找到目标
Last Updated:
Contributors: leeguooooo