JavaScript 二叉树

什么是二叉树?

一幅图胜过千言万语:
在这里插入图片描述

什么是排序二叉树(平衡二叉树/二叉搜索树)?

首先,排序二叉树肯定是一颗二叉树,然后它还需要满足一些条件:
如果一个节点有左孩子,那它左孩子的节点值一定小于它,如果一个节点有右孩子,那它右孩子的节点值一定大于它,然后它的左右孩子也是一颗排序二叉树,上图就是一颗平衡二叉树

如何构建排序二叉树

给定一个数组,[8, 3, 10, 1, 6, 14, 4, 7, 13] 作为节点值

var nodes = [8, 3, 10, 1, 6, 14, 4, 7, 13];
      var binaryTree = new BinaryTree();
      nodes.forEach(function(key) {
        binaryTree.insert(key);
      });

通过调用节点插入函数构建一颗排序二叉树,首先,将数组中每个值生成节点,以此插入,第一个插入的节点作为根节点。

//插入节点的函数接口
        this.insert = function(key) {
          var newNode = new Node(key);
          //如果此时是插入第一个节点,则将其作为根节点
          if (root === null) {
            root = newNode;
          } else {
            insertNode(root, newNode);
          }
        };

如果树不为空,则将待插入的节点通过比较,将其插入到相应位置。具体情况看下面 的代码注释。

var insertNode = function(node, newNode) {
          //插入节点小于当前节点
          if (newNode.key < node.key) {
            //当前节点没有左孩子,则将插入节点作为其左孩子
            if (node.left == null) {
              node.left = newNode;
            }
            //否则,插入其左边部分
            else {
              insertNode(node.left, newNode);
            }
          }
          //插入节点大于当前节点值情况同理
          else {
            if (node.right == null) {
              node.right = newNode;
            } else {
              insertNode(node.right, newNode);
            }
          }
        };

二叉树的前中后序遍历

前序遍历:根 左 右
中序遍历:左 根 右
后序遍历:左 右 根
这里我们以前序遍历为例子实现:

 //前序遍历接口函数
        this.preOrderTraverse = function(callback) {
          preOrderTraverseNode(root, callback);
        };

callback 函数理解为回调函数,用于在控制台上输出遍历的节点

 //回调函数
      var callback = function(key) {
        console.log(key);
      };

前序遍历节点过程的实现

var preOrderTraverseNode = function(node, callback) {
          if (node != null) {
            callback(node.key);
            preOrderTraverseNode(node.left, callback);
            preOrderTraverseNode(node.right, callback);
          }
        };

根据前序遍历的例子写出中序遍历和后序遍历的代码应该不难,这里就不贴了,在最后面会给出整个的功能实现代码

二叉树节点的查找

首先查找最小值节点,只需从根节点开始查找,只要当前节点有左节点,就将其更新为其左节点,直到它没有左节点,根据二叉排序树的构建原理,那它就是最小的。
同理,查找最大值节点,只需从根节点开始查找,只要当前节点含有右节点,就将其更新为其右节点,直到它没有右节点为止,它就是最大值节点。

 //查找最小值接口函数
        this.min = function() {
          return minNode(root);
        };
        //查找最大值接口函数
        this.max = function() {
          return maxNode(root);
        };
  //查早最小节点
        var minNode = function(node) {
          if (node) {
            while (node && node.left != null) {
              node = node.left;
            }
            return node.key;
          }
          return null;
        };
        //查找最大节点
        var maxNode = function(node) {
          if (node) {
            while (node && node.right != null) {
              node = node.right;
            }
            return node.key;
          }
          return null;
        };

查找一般的节点,如果存在,则返回true,否则返回false,这个需要有一个参数,key通过 key与当前节点值(从根节点开始)node.key比较,如果比当前节点值小,则在当前节点的左子树中寻找,如果比当前节点值大,则往当前节点的右子树中寻找。递归调用search函数。

//查找是否存在给定值节点接口函数
        this.search = function(key) {
          return searchNode(root, key);
        };
 //查找是否存在给定值节点
        var searchNode = function(node, key) {
          if (node == null) {
            return false;
          }
          if (key < node.key) {
            return searchNode(node.left, key);
          } else if (key > node.key) {
            return searchNode(node.right, key);
          } else {
            return true;
          }
        };

删除排序二叉树结点

这个又需要分情况讨论了,我们还是看到这幅图:

在这里插入图片描述
注意,我们删除结点之后还要保证它是一颗排序二叉树,即大小关系不变

  • 删除的是叶子结点:如 1
  • 删除的结点只有左子树或者只有右子树 ,如 14,这种情况只需将当前结点替换为其左子树或者右子树即可
  • 删除的 结点同时具有左右孩子树 如 3 ,查找其右子树的最小结点值,并用其替换当前结点值3 ,再删除最小值结点。
 //删除节点操作
        var removeNode = function(node, key) {
          if (node == null) {
            return null;
          }
          if (key < node.key) {
            node.left = removeNode(node.left, key);
            return node;
          } else if (key > node.key) {
            node.right = removeNode(node.right, key);
            return node;
          } else {
            //要删除的节点没有左右子树,即为叶子节点
            if (node.left === null && node.right === null) {
              node = null;
              return node;
            }
            //要删除的节点只有右子树
            if (node.left === null) {
              node = node.right;
              return node;
            }
            //要删除的节点只有左子树
            else if (node.right === null) {
              node = node.left;
              return node;
            }
            //删除的节点是含有两个孩子的节点
            var temp = findMinNode(node.right); //找到最小值节点
            node.key = temp.key; //替换要删除的节点值
            node.right = removeNode(node.right, temp.key); //从该节点的右子树中删除最小值节点
            return node;
          }
        };

整个的二叉树功能实现代码

可以用浏览器控制台,采用单步调试对代码进行验证

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Binary Trees</title>
  </head>
  <body>
    <script>
      function BinaryTree() {
        var Node = function(key) {
          this.key = key;
          this.left = null;
          this.right = null;
        };

        var root = null;
        //节点插入逻辑设计
        var insertNode = function(node, newNode) {
          //插入节点小于当前节点
          if (newNode.key < node.key) {
            //当前节点没有左孩子,则将插入节点作为其左孩子
            if (node.left == null) {
              node.left = newNode;
            }
            //否则,插入其左边部分
            else {
              insertNode(node.left, newNode);
            }
          }
          //插入节点大于当前节点值情况同理
          else {
            if (node.right == null) {
              node.right = newNode;
            } else {
              insertNode(node.right, newNode);
            }
          }
        };

        //插入节点的函数接口
        this.insert = function(key) {
          var newNode = new Node(key);
          //如果此时是插入第一个节点,则将其作为根节点
          if (root === null) {
            root = newNode;
          } else {
            insertNode(root, newNode);
          }
        };

        //中序遍历二叉树
        var inOrderTraverseNode = function(node, callback) {
          if (node != null) {
            inOrderTraverseNode(node.left, callback);
            callback(node.key);
            inOrderTraverseNode(node.right, callback);
          }
        };
        //前序遍历二叉树
        var preOrderTraverseNode = function(node, callback) {
          if (node != null) {
            callback(node.key);
            preOrderTraverseNode(node.left, callback);
            preOrderTraverseNode(node.right, callback);
          }
        };

        //后序遍历二叉树
        var postOrderTraverseNode = function(node, callback) {
          if (node != null) {
            postOrderTraverseNode(node.left, callback);
            postOrderTraverseNode(node.right, callback);
            callback(node.key);
          }
        };
        //查早最小节点
        var minNode = function(node) {
          if (node) {
            while (node && node.left != null) {
              node = node.left;
            }
            return node.key;
          }
          return null;
        };
        //查找最大节点
        var maxNode = function(node) {
          if (node) {
            while (node && node.right != null) {
              node = node.right;
            }
            return node.key;
          }
          return null;
        };
        //查找是否存在给定值节点
        var searchNode = function(node, key) {
          if (node == null) {
            return false;
          }
          if (key < node.key) {
            return searchNode(node.left, key);
          } else if (key > node.key) {
            return searchNode(node.right, key);
          } else {
            return true;
          }
        };
        //寻找最小值节点
        var findMinNode = function(node) {
          if (node) {
            while (node && node.left) {
              node = node.left;
            }
            //注意,跟上面的不同,此处是直接返回最小值节点,而不是最小值
            return node;
          }
          return null;
        };
        //删除节点操作
        var removeNode = function(node, key) {
          if (node == null) {
            return null;
          }
          if (key < node.key) {
            node.left = removeNode(node.left, key);
            return node;
          } else if (key > node.key) {
            node.right = removeNode(node.right, key);
            return node;
          } else {
            //要删除的节点没有左右子树,即为叶子节点
            if (node.left === null && node.right === null) {
              node = null;
              return node;
            }
            //要删除的节点只有右子树
            if (node.left === null) {
              node = node.right;
              return node;
            }
            //要删除的节点只有左子树
            else if (node.right === null) {
              node = node.left;
              return node;
            }
            //删除的节点是含有两个孩子的节点
            var temp = findMinNode(node.right); //找到最小值节点
            node.key = temp.key; //替换要删除的节点值
            node.right = removeNode(node.right, temp.key); //从该节点的右子树中删除最小值节点
            return node;
          }
        };
        //中序遍历函数接口
        this.inOrderTraverse = function(callback) {
          inOrderTraverseNode(root, callback);
        };
        //前序遍历接口函数
        this.preOrderTraverse = function(callback) {
          preOrderTraverseNode(root, callback);
        };
        //后序遍历接口函数
        this.postOrderTraverse = function(callback) {
          postOrderTraverseNode(root, callback);
        };
        //查找最小值接口函数
        this.min = function() {
          return minNode(root);
        };
        //查找最大值接口函数
        this.max = function() {
          return maxNode(root);
        };
        //查找是否存在给定值节点接口函数
        this.search = function(key) {
          return searchNode(root, key);
        };
        //删除叶子节点接口函数
        this.remove = function(key) {
          removeNode(root, key);
        };
      }
      //保存二叉树节点数值
      var nodes = [8, 3, 10, 1, 6, 14, 4, 7, 13];
      var binaryTree = new BinaryTree();
      nodes.forEach(function(key) {
        binaryTree.insert(key);
      });
      //回调函数
      var callback = function(key) {
        console.log(key);
      };
      //   binaryTree.inOrderTraverse(callback);
      //   binaryTree.preOrderTraverse(callback);
      //   binaryTree.postOrderTraverse(callback);
      //   console.log("the minNode is :" + binaryTree.min());
      //   console.log("the maxNode is :" + binaryTree.max());
      //   console.log(binaryTree.search(7)?'key 7 is found':'key 7 is not found');
      //   console.log(binaryTree.search(9)?'key 9 is found':'key 9 is not found');
      //   binaryTree.remove(1);
      binaryTree.remove(3);
    </script>
  </body>
</html>

因为有些功能,比如删除结点 的操作效果需要自行单步调试才可以理解地更加彻底,建议自己调试一下。
在这里插入图片描述

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值