本文最后更新于 2024-03-23T16:23:55+00:00
                  
                  
                
              
            
            
              
                
                思考:既然有了高效的散列表,那使用二叉树的地方是不是都可以替换成散列表呢?有没有哪些地方是散列表做不了,必须要用二叉树来做的呢?
主要内容
二叉查找(搜索)树
定义:树中的任意节点,其左子树每个节点的值要小于该节点,而右子树每个节点的值要大于该节点

特性:支持动态数据的快速查找、插入、删除操作
查找操作
查找逻辑:查找值与根节点,若等于则返回,若小于则在左子树中递归找,否则在右子树中递归找

1 2 3 4 5 6 7 8 9 10 11
   | function searchTree(root, targetValue) {   let p = root
    while(p) {     if(targetValue > p.value) p = p.right     else if(targetValue < p.value) p = p.left     else return p   }
    return null }
 
  | 
 
时间复杂度:最好 O(1),最坏 O(n),平均 O(logn)
插入操作
新插入的数据一般都是在叶子节点上,所以从根节点开始比较,找到插入位置。
如果插入数据比节点大,若节点的右子树为空则插入,否则再递归遍历右子树,继续找:【如果插入数据比节点大,若节点的右子树为空则插入】
如果插入数据比节点小,若节点的左子树为空则插入,否则再递归遍历左子树,继续找:【如果插入数据比节点小,若节点的左子树为空则插入,否则再递归遍历左子树】

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | function insertTree(root, targetValue) {   let p = root
    while(p) {     if(targetValue > p.value) {       if(p.right === null) {         p.right = targetValue         return       }       p = p.right     }     else if(targetValue < p.value){       if(p.left === null) {         p.left = targetValue         return       }       p = p.left     }   } }
 
  | 
 
时间复杂度:最好 O(1),最坏 O(n),平均 O(logn)
删除操作
三种情况:
1、若删除的节点,无子节点,则直接将父节点的指针改为 null,完成删除;比如下图删除 55
2、若删除的节点,有一个子节点(左或右),则直接将父节点的指针指向子节点,完成删除;比如下图删除 13
3、若删除的节点,有两个子节点,则先在右子树中找到最小的值,然后和该节点互换,之后再按照【1、2】情况删除该节点;比如下图删除 18

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
   | function deleteTree(root, tragetValue) {   let p = root    let pp = null 
    while(p) {     if(targetValue === p.value) {              return p     } else {              pp = p       if(targetValue > p.value) p = p.right       else p = p.left     }   }
       if(p.right && p.left) {     let minp = p.right      let minpp = null      while(minp.left) {       minpp = minp       minp = minp.left     }
      p.data = minp.data      p = minp     pp = minpp   }
    let child
    if(p.right) child = p.right    else if(p.left) child = p.left    else child = null 
       if(pp === null) p = child    else if(pp.left === p) pp.left = child    else pp.right = child  }
 
  | 
 
时间复杂度:最好 O(1),最坏 O(n),平均 O(logn)
解答思考
思考:既然有了高效的散列表,那使用二叉树的地方是不是都可以替换成散列表呢?有没有哪些地方是散列表做不了,必须要用二叉树来做的呢?

内容总结
二叉查找树:结构为 左子节点 < 节点 < 右子节点
其操作的时间复杂度跟树高度成正比,最坏为 O(n),平均为 O(logn)
新的思考
求一棵给定二叉树的高度?
根节点高度 = 根节点到叶子节点的最大边数
 高度为:4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   |  function heightOfBinaryTree(root, type = "tree") {   if (root === null) return 0; 
       let leftHeight = heightOfBinaryTree(root.left, "left");   let rightHeight = heightOfBinaryTree(root.right, "right");
       const height = Math.max(leftHeight, rightHeight) + 1;
    return height; }
 
  function TreeNode(val, left = null, right = null) {   this.val = val === undefined ? 0 : val;   this.left = left === undefined ? null : left;   this.right = right === undefined ? null : right; }
  const tree = new TreeNode(   1,   new TreeNode(2, new TreeNode(4), new TreeNode(5)),   new TreeNode(3) ); console.log(heightOfBinaryTree(tree)); 
 
  |