javascript算法之二叉搜索树的示例代码


Posted in Javascript onSeptember 12, 2017

什么是二叉树

二叉树就是树的每个节点最多只能有两个子节点

什么是二叉搜索树

二叉搜索树在二叉树的基础上,多了一个条件,就是二叉树在插入值时,若插入值比当前节点小,就插入到左节点,否则插入到右节点;若插入过程中,左节点或右节点已经存在,那么继续按如上规则比较,直到遇到一个新的节点。

二叉搜索树的特性

二叉搜索树由于其独特的数据结构,使得其无论在增删,还是查找,时间复杂度都是O(h),h为二叉树的高度。因此二叉树应该尽量的矮,即左右节点尽量平衡。

二叉搜索树的构造

要构造二叉搜索树,首先要构造二叉树的节点类。由二叉树的特点可知,每个节点类都有一个左节点,右节点以及值本身,因此节点类如下:

class Node {
 constructor(key) {
  this.key = key;
  this.left = null;
  this.right = null;
 }
}

接着构造二叉搜索树

class Tree{
 constructor(param = null) {
  if (param) {
   this.root = new Node(param);
  } else {
   this.root = null;
  }
 }
}

这里this.root就是当前对象的树。

二叉搜索树的新增

由二叉搜索树左子树比节点小,右子树别节点大的特点,可以很简单的写出二叉搜索树新增的算法,如下:

insert(key) {
 if (this.root === null) {
  this.root = new Node(key);
 } else {
  this._insertNode(this.root, key);
 }
}
_insertNode(node, key) {
 if (key < node.key) {
  if (node.left === null) {
   node.left = new Node(key);{1}
  } else {
   this._insertNode(node.left, key);{2}
  }
 } else if (key > node.key) {
  if (node.right === null) {
   node.right = new Node(key);{3}
  } else {
   this._insertNode(node.right, key);{4}
  }
 }
}

如上代码先判断新增的key与当前节点的key大小,如果小,就递归遍历左子节点,直到找到一个为null的左子节点;如果比当前节点大同理。如上代码{1}{2}{3}{4}之所以能改变this.root的值,是由于JavaScript函数是按值传递,而当参数是非基本类型时,例如这里的对象,其对象的值为内存,因此每次都会直接改变this.root的内容。

二叉搜索树的遍历

二叉搜索树分为先序、中序、后序三种遍历方式。

inOrderTraverse(callback) {
 this._inOrderTraverse(this.root, callback);
}
_inOrderTraverse(node, callback) {
 if (node) {
  this._inOrderTraverse(node.left, callback);
  callback(node.key);
  this._inOrderTraverse(node.right, callback);
 }
}

如上是中序遍历。

这里需要理解的一点是递归。要知道,函数的执行可以抽象为一种数据结构——栈。针对函数的执行,会维护一个栈,来存储函数的执行。函数在每一次递归时,都会将当前的执行环境入栈并记录执行的位置。以上述代码为例,有如下一个数据

其会从11开始,执行{1}入栈,然后进入7,接着执行{1}入栈,然后到5,执行{1}入栈,再到3,执行{1}入栈,此时发现节点3的左子节点为null,因此开始出栈,此时弹出节点3的执行环境,执行{2},{3},发现3的右侧子节点也为null,{3}的递归执行完毕,接着弹出节点5,执行{2}{3},接着弹出7,执行{2}{3}入栈,执行{3}时,发现节点7有右节点,因此继续执行{1},到节点8,再执行{1},8没有左子节点,{1}执行完毕,执行{2}{3},以此类推。

而前序与中序的不同点在于其先访问节点本身,也就是代码的执行顺序为 2 1 3。

后序同理,执行顺序为1 3 2

不难发现,无论前中后序,永远都是先递归左节点,当左节点遍历完毕时再弹出栈,遍历有节点。他们唯一不同的点在与访问该节点本身的时机。

二叉搜索树的查找

查找很简单,根据左子节点比该节点小,右子节点比该节点大的原则进行循环判断即可。

search(value) {
 if (this.root) {
  if (value === this.root.key) {
   return true;
  } else {
   return this._searchNode(value, this.root);
  }
 }
 throw new Error('this.root 不存在');
}
_searchNode(value, node) {
 if (!node) {
  return false;
 }
 if (value === node.key) {
  return true;
 }
 if (value > node.key) {
  return this._searchNode(value, node.right);
 } else if (value < node.key) {
  return this._searchNode(value, node.left);
 }
}

二叉搜索树的删除

删除较为复杂,需要根据不同情况判断

首先判断该节点是否有左子树,如果没有左子节树,则直接将右子树的根节点替换被删除节点;

如果有,则将右子树的最小节点替换被删除节点;

remove(key) {
 this._removeNode(this.root, key);
}
_removeNode(node, value) {
 if (!node) {
  return null;
 }
 if (value > node.key) {
  node.right = this._removeNode(node.right, value);
 } else if (value < node.key) {
  node.left = this._removeNode(node.left, value);
 } else {
  // 如果没有左子树,那么将右子树根节点作为替换节点
  if (!node.left) {
   return node.right;
   // 如果存在左子树,那么取右子树最小节点作为替换节点
  } else if (node.left) {
   return this._minNode(node.right);
  }
 }
}

总结

总的来说,通过这次简单的二叉搜索树的学习,让我重新认识了递归,以前对于递归的理解只是一些简单的理论概念,这次深入实践让我对递归的理解又加深了许多。

这让我想到了数学的学习,数学的理论公式是很容易记住掌握的,如果说对一个知识点的掌握满分是十分,那么直到真正去实践它之前,只看公式的掌握只能是2分,因为公式很简单,就几句话几个原则,但是遇到的问题是千变万化的,只有真正将理论付诸实践,经过各种实践的打磨蹂躏,才能真正理解它其中的奥秘。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
prototype Element学习笔记(篇二)
Oct 26 Javascript
JavaScript 类似flash效果的立体图片浏览器
Feb 08 Javascript
jQuery中使用了document和window哪些属性和方法小结
Sep 13 Javascript
js加载读取内容及显示与隐藏div示例
Feb 13 Javascript
JS实现星星评分功能实例代码(两种方法)
Jun 09 Javascript
轻松掌握JavaScript状态模式
Sep 07 Javascript
jQuery实现的多张图无缝滚动效果【测试可用】
Sep 12 Javascript
JS中判断null的方法分析
Nov 21 Javascript
微信小程序局部刷新触发整页刷新效果的实现代码
Nov 21 Javascript
在React中写一个Animation组件为组件进入和离开加上动画/过度效果
Jun 24 Javascript
解决Idea、WebStorm下使用Vue cli脚手架项目无法使用Webpack别名的问题
Oct 11 Javascript
vue项目中播放rtmp视频文件流的方法
Sep 17 Javascript
vue-resouce设置请求头的三种方法
Sep 12 #Javascript
vue params、query传参使用详解
Sep 12 #Javascript
Node.js中使用mongoose操作mongodb数据库的方法
Sep 12 #Javascript
vue proxyTable 接口跨域请求调试的示例
Sep 12 #Javascript
js封装成插件_Canvas统计图插件编写实例
Sep 12 #Javascript
JS监控关闭浏览器操作的实例详解
Sep 12 #Javascript
详解angular笔记路由之angular-router
Sep 12 #Javascript
You might like
IIS6.0+PHP5.x+MySQL5.x+Zend3.0x+GD+phpMyAdmin2.8x通用安装实例(已经完成)
2006/12/06 PHP
利用php递归实现无限分类 格式化数组的详解
2013/06/08 PHP
php简单实现查询数据库返回json数据
2015/04/16 PHP
详解php curl带有csrf-token验证模拟提交方法
2018/04/18 PHP
基于jquery的仿百度的鼠标移入图片抖动效果
2010/09/17 Javascript
window.location.hash 使用说明
2010/11/08 Javascript
javascript开发随笔一 preventDefault的必要
2011/11/25 Javascript
js兼容火狐显示上传图片预览效果的方法
2015/05/21 Javascript
Angularjs 自定义服务的三种方式(推荐)
2016/08/02 Javascript
jQuery Ajax传值到Servlet出现乱码问题的解决方法
2016/10/09 Javascript
Node.js常用工具之util模块
2017/03/09 Javascript
Nodejs读取文件时相对路径的正确写法(使用fs模块)
2017/04/27 NodeJs
基于js原生和ajax的get和post方法以及jsonp的原生写法实例
2017/10/16 Javascript
vue+SSM实现验证码功能
2018/12/07 Javascript
python实现超简单的视频对象提取功能
2018/06/04 Python
浅谈pycharm出现卡顿的解决方法
2018/12/03 Python
PyQt5 多窗口连接实例
2019/06/19 Python
Python中的四种交换数值的方法解析
2019/11/18 Python
详解python 破解网站反爬虫的两种简单方法
2020/02/09 Python
PyCharm Anaconda配置PyQt5开发环境及创建项目的教程详解
2020/03/24 Python
基于Python3读写INI配置文件过程解析
2020/07/23 Python
美国睫毛、眉毛精华液领导品牌:RevitaLash Cosmetics
2018/03/26 全球购物
Wojas罗马尼亚网站:波兰皮鞋品牌
2018/11/01 全球购物
精彩的广告词
2014/03/19 职场文书
财务管理专业求职信
2014/06/11 职场文书
党的群众路线对照检查材料思想汇报(学校)
2014/10/04 职场文书
2014年房地产个人工作总结
2014/12/20 职场文书
单身申明具结书
2015/02/26 职场文书
2015小学教师年度工作总结
2015/05/12 职场文书
小学生家长意见
2015/06/03 职场文书
2016年“9.22”世界无车日活动小结
2016/04/05 职场文书
《刺客之王:C罗全景传记》:时代从来不会亏待手艺人
2019/11/28 职场文书
redis配置文件中常用配置详解
2021/04/14 Redis
基于Golang 高并发问题的解决方案
2021/05/08 Golang
springcloud之Feign超时问题的解决
2021/06/24 Java/Android
Windows server 2016服务器基本设置
2022/08/14 Servers