JS - 二叉树算法实现与遍历 (更新中...)
一、关于二叉树:
截图来自:https://segmentfault.com/a/1190000000740261
温馨提示:学习以及使用二叉树概念,心中永远有这么一个图,对于理解和接受二叉树有很大的帮助。
截图来自慕课:http://www.imooc.com/video/15749
关于二叉树概念,百度百科一大篇,百度一大堆,我也是看各种博客文章;
贴几个传送门
- https://segmentfault.com/a/1190000000740261
- http://code.tutsplus.com/articles/data-structures-with-javascript-tree--cms-23393
- https://www.nczonline.net/blog/2009/06/09/computer-science-in-javascript-binary-search-tree-part-1/
二、整理的名词:
- 二叉树的高:二叉树的层级就是二叉树的高,有几层就是高是多少
- 二叉树的根:二叉树最上边没有父亲节点的第一个节点就是整个二叉树的根节点
- 二叉树的叶子:二叉树最下边没有孩子节点的最后一层的节点就是二叉树的叶子(就像一棵树的叶子,最末端)
- 二叉树特点:左边的孩子小于右边孩子的值,左边孩子值小于父亲节点的值,右边孩子的值大于父亲节点的值。【又称排序二叉树】
三、二叉树的实现
——用javascript生成一个二叉树:
代码:
- function BinaryTree(){
- var Node = function(key){
- this.key = key;
- this.left = null;
- this.right = null;
- };
- var oRoot = 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(!(oRoot === null)){
- insertNode(oRoot,newNode)
- }else{
- oRoot = newNode;
- }
- };
- }
- var nodeArr = [8,3,10,1,6,14,4,7,13];
- var binaryTree = new BinaryTree();
- nodeArr.forEach(function(key){
- binaryTree.insert(key);
- })
javascript实现二叉树结构
ps:因为是看慕课的视频跟着做的,我把代码整理下来分析了一次、以下贴分析的代码:
- function BinaryTree(){//封装一个二叉树的函数
- var Node = function(key){//设置一个节点函数,根节点的key是值,left表示左箭头,right表示右箭头
- this.key = key;
- this.left = null;
- this.right = null;
- };
- var oRoot = 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);//传进来的key值,作为新值
- if(!(oRoot === null)){//如果第一层的根节点不为空
- insertNode(oRoot,newNode);//往根节点的下一层插入去,执行“寻找节点位置并插入节点”函数,根据二叉树的数据结构特点和原则【根节点左边的元素值比根节点小,右边的元素值比根节点大】判断性的寻找正确的位置并插入
- }else{
- oRoot = newNode;//根节点为空,就是整个树还为空,那就让这个值作为根节点占住根节点的位、
- }
- };
- }
- var nodeArr = [8,3,10,1,6,14,4,7,13];//要遍历的数组
- var binaryTree = new BinaryTree();//把整个自定义的二叉树函数送给这个变量指针
- nodeArr.forEach(function(key){//遍历数组,并传入要遍历的值
- binaryTree.insert(key);//执行二叉树函数的"插入根节点"函数,开始插入函数。
- })
四、二叉树的遍历
中序遍历
顺序(左中右):先访问当前节点的左子树直到左子树为空,再打印当前值,最后访问当前节点的右子树
作用:用于排序一个数组,从小到大升序排列。
前序遍历
顺序(中左右):先访问并打印当前节点的值,然后访问当前节点的左子树,最后访问当前节点的右子树
作用:复制一个已有的二叉树结构,性能是最高的。比重新创造一个新的二叉树的效率高十倍多。
后序遍历
顺序(左右中):先访问当前节点的左子树,然后访问当前节点的右子树,最后访问并打印当前节点值
作用:用于操作系统和文件系统的遍历上。
1、中序遍历
源码:
- // 中序遍历函数封装
- var inOrderTraverseNode = function(node,callback){
- if(node !== null){
- inOrderTraverseNode(node.left,callback);//这种在函数身体里边再调用函数自身的方法真的要注意使用!
- callback(node.key);
- inOrderTraverseNode(node.right,callback);
- }
- }
- // 增加遍历的api接口
- this.inOrderTraverse = function(callback){
- inOrderTraverseNode(oRoot,callback);
- }
中序遍历 Code
中序遍历源码解析
- <script>
- //在原来二叉树函数的基础上继续补充
- function BinaryTree(){
- // 中序遍历函数封装
- var inOrderTraverseNode = function(node,callback){//接收根节点的值和回调函数
- if(node !== null){//首先判断,二叉树的当前节点里边有没有内容
- inOrderTraverseNode(node.left,callback);//如果该节点左边有分节点(左边有孩子)那么在左孩子的位置基础上,再次调用中序遍历函数,并把当前左孩子的值传给函数。直到找不到左孩子时这个才会停止,函数会一层一层的嵌套。那么,直到最后一层的叶子级别的左孩子,就不会继续调用这个函数了,也就是会遍历到树的最左下角的那个节点了。
- //这种在函数身体里边再调用函数自身的方法真的要注意使用!
- callback(node.key);//输出当前的值
- inOrderTraverseNode(node.right,callback);//再看看这个节点上有没有右节点,如果有,就站在右节点的位置上,再次执行这个函数,那么下次遍历的就会是这个右节点的左孩子是否存在了。
- }
- }
- // 增加遍历的api接口
- this.inOrderTraverse = function(callback){//后期调用
- inOrderTraverseNode(oRoot,callback);//接口内,调用中序遍历函数,并且传入当前的根节点的值和回调函数。传oRoot是因为要从根节点开始。
- }
- };
- var callback = function(key){// 增加遍历的自定义效果的回调函数
- console.log(key);//每次遍历后输出当前的遍历值。
- };
- binaryTree.inOrderTraverse(callback);// 调用封装好的遍历方法api,以实现遍历二叉树的目标。
- </script>
2、前序遍历
(ps:由于和中序遍历的结构基本一致,只是遍历的顺序稍有改变,不再做具体的分析。)
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>二叉树的前序遍历</title>
- <meta name="author" content="xing.org1^">
- <meta name="description" content="二叉树的遍历之前序遍历法。">
- <meta name="description" content="中序遍历的作用是数组从小到大按照升序排列,前序遍历的作用是用来复制一个已有的二叉树结构,利用性能是最高的。用前序遍历复制的二叉树,效率要比重新构造一个二叉树高得多">
- </head>
- <body>
- <h3>前序遍历的特点就是遍历次序的不一样,先打印当前节点,然后访问当前节点的左子树,再然后打印当前节点的右子树</h3>
- <h4>用前序遍历拷贝一个二叉树,只需要依次遍历所有的子节点就好了。</h4>
- <script>
- var arr = [1,8,34,67,23,2];
- var newArr = [];
- var binaryTree = new BinaryTree();
- console.log("原数组为:"+arr)
- var callBack = function(key){
- console.log("复制——"+key);
- newArr.push(key);
- }
- arr.forEach(function(key){
- binaryTree.startCreateBinary(key);
- });
- binaryTree.startTraverseBinary(callBack);
- console.log("最后复制出数组为:"+newArr);
- // 二叉树
- function BinaryTree(){
- var Node = function(key){
- this.key = key;
- this.left = null;
- this.right = null;
- };
- var oRoot = null;
- this.startCreateBinary = function(key){
- var newNode = new Node(key);
- if(oRoot){
- createBinary(oRoot,newNode)
- }else{
- oRoot = newNode;
- }
- }
- var createBinary = function(node,newNode){
- if(node.key > newNode.key){
- // 这里欠缺思考:如果当前值和根节点的值相等呢?放哪边?
- if(node.left){
- createBinary(node.left,newNode);
- }else{
- node.left = newNode;
- }
- }else{
- if(node.right){
- createBinary(node.right,newNode);
- }else{
- node.right = newNode;
- }
- }
- };
- this.startTraverseBinary = function(callback){
- traverseBinary(oRoot,callback);
- };
- // 前序遍历
- var traverseBinary = function(node,callback){
- if(node){
- callback(node.key);
- traverseBinary(node.left,callback);
- traverseBinary(node.right,callback);
- }
- };
- }
- </script>
- </body>
- </html>
二叉树结构生成和前序遍历
3、后序遍历
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="author" content="xing.org1^">
- <meta name="description" content="二叉树的遍历之后序遍历法。">
- <title>后序遍历</title>
- </head>
- <body>
- <h3>访问顺序:先访问左子树,在访问右子树,最后访问当前节点的值</h3>
- <h3>作用:应用到操作系统、文件系统的遍历之中</h3>
- <h3>由下边代码可以看到,他把数组给反过来了,反着输出了一遍</h3>
- <script>
- var arr = [23,1,82,845,9,25,0];
- var newArr = [];
- var binaryTree = new BinaryTree();
- var callBack = function(key){
- console.log(key);
- newArr.push(key);
- }
- arr.forEach(function(key){
- binaryTree.startCreateBinary(key);
- });
- binaryTree.startTraveralBinary(callBack);
- console.log(newArr);
- // 二叉树
- function BinaryTree(){
- var Node = function(key){
- this.key = key;
- this.left = null;
- this.right = null;
- };
- // 创建
- var oRoot = null;
- this.startCreateBinary = function(key){
- var newNode = new Node(key);
- if(oRoot){
- createBinary(oRoot,newNode);
- }else{
- oRoot = newNode;
- }
- }
- var createBinary = function(node,newNode){
- if(node.key > newNode.key){//这里特别注意,比较的是值的大小,不是两个函数,一定要加.key属性!!!
- if(node.left){
- createBinary(node.left,newNode);
- }else{
- node.left = newNode;
- }
- }else{
- if(node.right){
- createBinary(node.right,newNode);
- }else{
- node.right = newNode;
- }
- }
- };
- // 遍历
- this.startTraveralBinary = function(callback){
- traveralBinary(oRoot,callback);
- }
- var traveralBinary = function(node,callback){
- if(node){
- traveralBinary(node.left,callback);
- traveralBinary(node.right,callback);
- callback(node.key);
- }else{
- console.log("没有结构,不用遍历!");
- }
- };
- };
- </script>
- </body>
- </html>
二叉树结构生成和后序遍历
二叉树的结构生成和中序遍历——示例解析:
中序遍历二叉树生成排序数组
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="author" content="郭菊锋">
- <meta name="description" content="二叉树中序遍历分析">
- <title>中序遍历二叉树生成排序数组</title>
- </head>
- <body>
- <script>
- function tree(){//封装一个二叉树的函数
- // 创建一个空的根节点,根节点的值是参数key,创建left表示左箭头,创建right表示右箭头
- var oNode = function(key){
- this.key = key;
- this.left = null;
- this.right = null;
- }
- // 开始构造二叉树 api
- // 创建并确定第一层的根节点
- var oRoot = null;//空的根节点,准备一个根节点的坑。
- this.startTree = function(key){//"插入根节点"函数,设置一个开始的接口方法
- var newNode = new oNode(key);//每次执行都创建一个新的节点,传进来的key值作为新值。
- if (!(oRoot === null)) {//如果第一层的根节点不为空
- binaryTree.createTree(oRoot,newNode)
- //第一个值以后的值,把他们当做新节点给了根的孩子们,让他们依次当爸爸妈妈。
- //往根节点的下一层插入去,执行“寻找节点位置并插入节点”函数,根据二叉树的数据结构特点和原则【根节点左边的元素值比根节点小,右边的元素值比根节点大】判断性的寻找正确的位置并插入
- } else {
- //else,根节点为空,就是整个树还为空,那就让这个值作为根节点占住根节点的位、
- oRoot = newNode;//把第一次创建的节点给了根。
- }
- }
- // 创造二叉树 api
- // 在子层依次按规律寻找位置存放新节点
- this.createTree = function(node,newNode){//设置一个创造的方法
- //“寻找节点位置并插入节点”函数,参数为当前节点和要加入的新节点
- // 因为二叉树的数据结构特点就是根节点左边的元素值比根节点小,右边的大、。
- console.log(newNode)
- if(newNode.key < node.key){//后边的新值比当前值小的话,放左边,否则放右边
- //如果新节点比当前节点小,那就扔到左边.特别注意这里比较的是节点的值,如果你错误地比较节点的话,将永远都是走else流程的。
- if(node.left === null){//扔到左边前,先看看左边有没有空位子,没位置也放不下,没位置就在左边这里继续和左边的值判断,如果小于左边的值,放左边值的孩子层的左边,反之大就放右边
- node.left = newNode;//左边有位子(即为空值),就把这个值扔到这里
- }else{
- binaryTree.createTree(node.left,newNode);//若左边没有空位子(即左边已经有了根节点,那么再次执行插入节点函数,同时比较的数变成左边这个根节点的值和新值的大小了。也就是站在node.left的基础上,以node.left作为新的根节点去查找他的下一级进行再次判断,看看newNode是放到他下边的左边还是右边
- //注意上边如果是var声明的函数,则不用binaryTree这么调,如果是通过this.加给binaryTree的方法,那么需要通过binaryTree来调用这个接口。
- }
- // 接下来同左边了
- }else{//如果新节点比当前节点大,那就扔到右边
- if(node.right === null){//扔到右边前,看看有没有地方
- node.right = newNode;//有地方直接扔进去
- }else{
- binaryTree.createTree(node.right,newNode);//没地方,那就重新遍历,遍历换成比较右边占着茅坑的这个值和要放进来的新值的大小
- }
- }
- };
- // 遍历二叉树的接口 api
- this.startLDR = function(callback){
- centerTraverse(oRoot,callback);//接口内,调用中序遍历函数,并且第一次调用,传入当前的根节点的值和回调函数。传oRoot是因为要从根节点开始。
- }
- // 遍历二叉树——中序遍历函数
- //中序遍历的顺序就是左根右,所以先node.left、再node.key、最后node.right
- var centerTraverse = function(node,callback){//用var声明(所以后期调用就不用父亲的权限了,区别于this声明)一个中序遍历函数,接收参数为节点的值和回调函数
- if(!(node === null)){//首先判断,二叉树的当前节点里边有没有内容
- centerTraverse(node.left,callback);//这种在函数身体里边再调用函数自身的方法真的要注意使用!
- //如果该节点左边有分节点(左边有孩子)那么在左孩子的位置基础上,再次调用中序遍历函数,并把当前左孩子的值传给函数。直到找不到左孩子时这个才会停止,函数会一层一层的嵌套。那么,直到最后一层的叶子级别的左孩子,就不会继续调用这个函数了,也就是会遍历到树的最左下角的那个节点了。
- callback(node.key);//得到当前遍历的值然后当参数传给callback函数,然后。看自己的需求,遍历到当前值时自己需要什么操作就写在callback里。我这里只用了console.log输出当前的值,和push把当前值放进新数组里以求得到顺序遍历后的新排序函数。
- centerTraverse(node.right,callback);//再看看这个节点上有没有右节点,如果有,就站在右节点的位置上,再次执行这个函数,那么下次遍历的就会是这个右节点的左孩子是否存在了。
- }
- }
- };
- // 执行二叉树,整理数组
- var arr = [8,3,16,12,18,39,20,23];//准备一个数组
- var binaryTree = new tree();//把封装好的二叉树函数给一个变量指针
- arr.forEach(function(key){//利用forEach循环函数,依次将数组中的值放在二叉树中
- binaryTree.startTree(key);
- })
- var aT = [];
- var callback = function(key){//回调函数,用于二叉树执行遍历后,接收返回的值。
- console.log(key);//依次输出值用于调试
- aT.push(key);//利用push把每次输出的值放到空数组中,生成一个新的排序数组
- }
- binaryTree.startLDR(callback);//调用遍历函数接口,执行遍历二叉树的命令
- console.log(aT);
- </script>
- </body>
- </html>
四、二叉树的节点查找:
1.查找最小值:
其实就是一直遍历,从根节点开始,找当前节点的左孩子,直到某个节点没有左孩子了,那么这个节点就是整个二叉树的最小值了
(因为左孩子里的值比节点的值小,右节点的比当前节点的大。这是一个固定规律。)
- if(node){
- if(node.left){
- node = node.left;
- return searchMinBinary(node);
- }else{
- return node.key;
- }
- }
- return null;
if方法
- if(node){
- while(node && node.left !== null){
- node = node.left;
- }
- return node.key;
- }
- return null
while方法
2.查找最大值:
同样的,从根节点一直向右找,找当前节点的右孩子,直到找到某个节点他没有右节点的时候,那么这个节点就是整个二叉树的最大值。
- if(node){
- while(node && node.right !== null){
- node = node.right;
- }
- return node.key;
- }
- return null;
while方法
3.查找对应值:
从根节点开始,先判断当前节点与给定节点的值是否一样,一样就是这个节点的值。
不一样就判断给定的值和当前节点的值的大小,
若给定值小于当前节点的值就向左(进入当前节点的左子树进行比对)查找,
若大于当前节点的值就向右(进入当前节点的右子树进行比较)查找。
若最终找不到给定的值,就是没有这个值,查找失败。
- if(node){
- if(node.key == newNode){
- return node.key;
- }else if(node.key > newNode){
- node = node.left;
- return searchBinary(node,newNode);
- }else{
- node = node.right;
- return searchBinary(node,newNode);
- }
- }
- console.log("没有结果");
if判断查找对应值
三种查找情况的示例代码及分析:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="author" content="xing.org1^">
- <meta name="description" content="二叉树的节点查找">
- <title>二叉树的节点查找</title>
- </head>
- <body>
- <h4>查找节点的理念就是:看这个节点是不是在二叉树当中。</h4>
- <h4>应用:广泛</h4>
- <h4>方法分为三种:</h4>
- <ol>
- <li>查找二叉树节点的最小值:其实就是一直遍历,从根节点开始,找当前节点的左孩子,直到某个节点没有左孩子了,那么这个节点就是整个二叉树的最小值了(因为左孩子里的值比节点的值小,右节点的比当前节点的大。这是一个固定规律。)</li>
- <li>查找二叉树节点的最大值:同样的,从根节点一直向右找,找当前节点的右孩子,直到找到某个节点他没有右节点的时候,那么这个节点就是整个二叉树的最大值。</li>
- <li>二叉树节点中查找给定的相应节点的值:从根节点开始,先判断当前节点与给定节点的值是否一样,一样就是,不一样就判断给定的值和当前节点的值的大小,若给定值小于当前节点的值就向左(进入当前节点的左子树进行比对)查找,若大于当前节点的值就向右(进入当前节点的右子树进行比较)查找。若最终找不到给定的值,就是没有这个值,查找失败。</li>
- </ol>
- <script>
- var arr = [3416,33,423,212,12];
- var binaryTree = new BinaryTree();
- arr.forEach(function(key){
- binaryTree.startCreateBinary(key);
- });
- // 调用接口,查找二叉树的最小值。
- var minArr = binaryTree.startSearchBinary("min");
- var maxArr = binaryTree.startSearchBinary("max");
- console.log("最小值为:"+minArr);
- console.log("最大值为:"+maxArr)
- var searchNum = binaryTree.startSearchBinaryNum(1);
- console.log(searchNum);
- // 二叉树
- function BinaryTree(){
- var Node = function(key){
- this.key = key;
- this.left = null;
- this.right = null;
- };
- var oRoot = null;
- this.startCreateBinary = function(key){
- var newNode = new Node(key);
- if(oRoot){
- createBinary(oRoot,newNode);
- }else{
- oRoot = newNode;
- }
- };
- var createBinary = function(node,newNode){
- if(node.key > newNode.key){//这里是比较两个值的大小,在这里栽坑十几次了!!!
- if(node.left){
- createBinary(node.left,newNode);
- }else{
- node.left = newNode;
- }
- }else{
- if(node.right){
- createBinary(node.right,newNode);
- }else{
- node.right = newNode;
- }
- }
- };
- // 查找并返回二叉树节点(最大值/最小值)
- this.startSearchBinary = function(type){
- if(type === "min"){
- return searchMinBinary(oRoot);
- }else{
- return searchMaxBinary(oRoot);
- }
- };
- var searchMinBinary = function(node){
- if(node){
- // 这里判断是否有左子树的方法除了while还可以用if方法!
- if(node.left){
- // 这里,当本节点有左子节点时,应该把左子树当做新的根节点重新查找
- node = node.left;//函数内部,数值身份的转换。
- return searchMinBinary(node);//这里依然递归调用的话,会导致虽然找到最小的1,但是找最小值的函数是嵌套在父元素的,等找完最小值后还会执行父节点剩下的函数,也就是后边return 2那里。所以最后返回的都将是2。所以解决方法是这里return出去,让他停止继续递归。霍哈哈哈,所以我自己写的if判断的写法是有用的。
- }else{
- // 查找左子树一直找不到的时候应该怎么办?返回当前值啊!就是node的key值啊!
- return node.key;
- }
- }
- return null;
- // while方法
- // if(node){
- // while(node && node.left !== null){
- // node = node.left;
- // }
- // return node.key;
- // }
- // return null//连二叉树都是空的话,那就直接返回空。
- };
- var searchMaxBinary = function(node){
- if(node){
- while(node && node.right !== null){
- node = node.right;
- }
- return node.key;
- }
- return null;
- };
- // 查找某个特定的值
- this.startSearchBinaryNum = function(key){
- return searchBinary(oRoot,key)
- }
- var searchBinary = function(node,newNode){
- if(node){
- if(node.key == newNode){
- return node.key;
- }else if(node.key > newNode){
- node = node.left;
- // searchBinary(node,newNode);//这种写法导致我每次都是嵌套遍历,然后最后结果都是console.log("没有结果");后来发现,这里也应该有return,不然即使找到了相应的值,但是语句还是会继续执行所以导致最后都是不正确的值!
- return searchBinary(node,newNode);
- }else{
- node = node.right;
- return searchBinary(node,newNode);
- }
- }
- console.log("没有结果");
- };
- }
- </script>
- </body>
- </html>
二叉树的节点查找示例分析
五、二叉树的节点删除:
1.删除叶子节点 --> 叶子节点的特点:左右子节点都为空;
方法:通过判断找到这个叶子节点(特点是:node.left === null && node.right === null),然后把这个节点改成node = null;就是删除了
- // 删除二叉树的叶子节点
- this.startRemoveNode = function(key){//创建一个删除的接口,传进一个要删除的值
- oRoot = removeLeafNode(oRoot,key);//调用删除方法,传入两个值,第一次要比较的根节点和传进来的值。
- };
- // 删除叶子节点方法
- var removeLeafNode = function(node,key){
- if(node === null){//判断整个二叉树是否为空
- return null;
- }//
- if(key < node.key){//判断要删除的值与当前节点的值是否相等
- node.left = removeNode(node.left,key);//如果不相等,那就站在node.left这个节点上,继续调用一遍删除节点的函数
- return node;//如果他的子节点刚好是要删除的那个,删完回来后返回node
- }else if(key > node.key){//同理node.left
- node.right = removeNode(node.right,key);
- return node;
- }else{//当前节点的值刚好等于要删除的值,
- if(node.left === null && node.right === null){//并且符合叶子节点的特点:左右子节点都为空;
- node = null;//把当前节点设为空表示删除这个节点了。
- return node;
- }
- }
- };
2.删除中间节点(即有子节点的节点)
1)如果左右孩子都是空(即叶子节点):直接删除 (node=null) 即可
代码见上边“删除叶子节点”。
2)左孩子为空:没有左孩子的情况,删了以后让父节点直接指向该节点的右孩子;
- if(node.left === null){//如果中间节点只有右孩子
- node = node.right;
- return node;
- }
3)右孩子为空:没有右孩子的情况,删了以后让父节点直接指向该节点的左孩子;
- if(node.right === null){//如果中间节点只有左孩子
- node = node.left;// 即右子节点为空,则将本节点等于他的左节点
- return node;
- }
4)如果左右孩子都存在:左右孩子都有,删除本节点后,找到右子节点里边,最左边的子节点(也就是右孩子下边最小的值,最底层的最小的左子孙子);让这个左子孩子的值等于当前值。再从新找到这个值删掉。(也就相当于把当前节点右孩子中最左边最小的叶子元素移动到并替换了当前节点);
- // 找到最小值节点
- var findMinNode = function(node){
- if(node) {
- while(node && node.left !== null){
- node = node.left;
- }
- return node;
- }
- return null;
- }
- // 找到这个中间节点的右孩子的最小的左孩子,然后把值复制下来,再把这个孩子删除掉(相当于把这个孩纸移动过来了)
- var aux = findMinNode(node.right);
- node.key = aux.key;
- node.right = removeNode(node.right,aux.key);
删除节点的方法汇总:
- <script>
- var arr = [23,15,54,1,6213,17];
- console.log(arr);
- var binaryTree = new BinaryTree();
- arr.forEach(function(key){
- binaryTree.startCreateBinary(key);
- });
- binaryTree.startRemoveNode(1);
- console.log(arr);
- function BinaryTree(){
- var Node = function(key){
- this.key = key;
- this.left = null;
- this.right = null;
- }
- var oRoot = null;
- this.startCreateBinary = function(key){
- var newNode = new Node(key);
- if(oRoot){
- createBinary(oRoot,newNode);
- }else{
- oRoot = newNode;
- }
- };
- var createBinary = function(node,newNode){
- if(node.key > newNode.key){
- if(node.left){
- createBinary(node.left,newNode);
- }else{
- node.left = newNode;
- }
- }else{
- if(node.right){
- createBinary(node.right,newNode);
- }else{
- node.right = newNode;
- }
- }
- };
- // 找到最小值节点
- var findMinNode = function(node){
- if(node) {
- while(node && node.left !== null){
- node = node.left;
- }
- return node;
- }
- return null;
- }
- // 删除二叉树的叶子节点
- this.startRemoveNode = function(removeKey){//创建一个删除的接口,传进一个要删除的值
- oRoot = removeNode(oRoot,removeKey);//调用删除方法,传入两个值,第一次要比较的根节点和传进来的值。
- };
- // 删除叶子节点方法+删除中间节点的方法
- var removeNode = function(node,removeKey){
- if(node === null){//判断整个二叉树是否为空
- return null;
- }//
- if(removeKey < node.key){//判断要删除的值与当前节点的值是否相等
- node.left = removeNode(node.left,removeKey);//如果不相等,那就站在node.left这个节点上,继续调用一遍删除节点的函数
- return node;//如果他的子节点刚好是要删除的那个,删完回来后返回node
- }else if(removeKey > node.key){//同理node.left
- node.right = removeNode(node.right,removeKey);
- 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 aux = findMinNode(node.right);
- node.key = aux.key;
- node.right = removeNode(node.right,aux.key);
- }
- };
- }
- </script>
~完
JS - 二叉树算法实现与遍历 (更新中...)的更多相关文章
- leetcode 257. 二叉树的所有路径 包含(二叉树的先序遍历、中序遍历、后序遍历)
给定一个二叉树,返回所有从根节点到叶子节点的路径. 说明: 叶子节点是指没有子节点的节点. 示例: 输入: 1 / \2 3 \ 5 输出: ["1->2->5", & ...
- 百度前端学院js课堂作业合集+分析(更新中...)
第一课:简陋的登录框 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...
- JS基础知识再整理..........不断更新中
1.JS的五种基本数据类型:字符串.数值.布尔.null.underfined. 2.在JS中,字符串.数值.布尔三种数据类型,有其属性和方法: 3.字符串的三种常用方法[.indexof()..su ...
- js 二叉树算法
//生成二叉树 function binarySearchTree() { let Node = function(key) { this.key = key; this.left = null; t ...
- 常用JS、jquery 命令(不断更新中)
设置用户粘贴板中的文本信息:window.clipboardData.setData('Text', location.href); 获取用户粘贴板中的文本信息: window.clipboardDa ...
- L2-006 树的遍历 (25 分) (根据后序遍历与中序遍历建二叉树)
题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805069361299456 L2-006 树的遍历 (25 分 ...
- PTA L2-006 树的遍历-二叉树的后序遍历+中序遍历,输出层序遍历 团体程序设计天梯赛-练习集
L2-006 树的遍历(25 分) 给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列.这里假设键值都是互不相等的正整数. 输入格式: 输入第一行给出一个正整数N(≤),是二叉树中结点的 ...
- [leetcode]_根据二叉树的先序遍历(后序遍历) + 中序遍历 重建二叉树
题目1:Construct Binary Tree from Preorder and Inorder Traversal 给定一棵二叉树的先序遍历和中序遍历,求重建二叉树. 思路: 1.先序遍历的第 ...
- js坑爹笔试题目汇总(持续更新中)
把你的面试官问倒,你就是一个合格的面试者了,以下总结一些易错的js笔试题目,会持续更新中.欢迎关注 1,考察this var length = 10 function fn(){ alert(this ...
随机推荐
- 每天一个linux命令:Linux 目录结构
对于每一个Linux学习者来说,了解Linux文件系统的目录结构,是学好Linux的至关重要的一步.,深入了解linux文件目录结构的标准和每个目录的详细功能,对于我们用好linux系统至关只要,下面 ...
- SSL 证书服务推荐
最近要用到ssl.故做了一些搜索 1.Let's Encrypt:免费,快捷,支持多域名(不是通配符),三条命令即时签署+导出证书.缺点是暂时只有三个月有效期,到期需续签. 2.StartSSL免费D ...
- maven2中snapshot快照库和release发布库的应用
在之前的文章中介绍了maven2中snapshot快照库和release发布库的区别和作用,我今天这里要介绍的是如何在项目中应用snapshot和release库,应用snapshot和release ...
- Maven最佳实践-distributionManagement
分发构件至远程仓库 mvn install 会将项目生成的构件安装到本地Maven仓库,mvn deploy 用来将项目生成的构件分发到远程Maven仓库.本地Maven仓库的构件只能供当前用户使用, ...
- 安卓开发笔记——打造万能适配器(Adapter)
为什么要打造万能适配器? 在安卓开发中,用到ListView和GridView的地方实在是太多了,系统默认给我们提供的适配器(ArrayAdapter,SimpleAdapter)经常不能满足我们的需 ...
- Android 代码自动提示功能
Eclipse for android 实现代码自动提示智能提示功能,介绍 Eclipse for android 编辑器中实现两种主要文件 java 与 xml 代码自动提示功能,解决 eclips ...
- CStringArray error C2248: 'CObject::CObject' : cannot access private member declared in class
在开发中将一个字符串分割,并将子字符串保存在CStringArray中,专门写了一个函数,如下: SplitStringToCString(CString str, TCHAR tszSplit, C ...
- kettle教程二
转载:http://www.cnblogs.com/limengqiang/archive/2013/01/16/KettleApply2.html 1.应用场景 这里简单概括一下几种具体的应用场景, ...
- v9定时发布的简单实现方法[支持静态生成]
将以下代码放到 api/count.php 文件最后 的 ?>之前 //add 定时发布审核功能 $modelid = $modelid ? $modelid : intval($_GET['m ...
- 用VS2012不能打开VS2010的项目
应该是装过sql2012或者sql2008,自带的那个visual studio,你可以直接在外面打开VS2012,然后用里面的文件去打开项目.而不要直接双击那个解决方案的文件.