javascript数据结构之多叉树经典操作示例【创建、添加、遍历、移除等】


Posted in Javascript onAugust 01, 2018

本文实例讲述了javascript数据结构之多叉树经典操作。分享给大家供大家参考,具体如下:

多叉树可以实现复杂的数据结构的存储,通过遍历方法可以方便高效的查找数据,提高查找的效率,同时方便管理节点数据。javascript的DOM其实就是以多叉树的形式存储的。下面用javascript来实现多叉树的数据结构

1、创造一个节点

数据是以节点的形式存储的:

class Node {
  constructor(data) {
    this.data = data;
    this.parent = null;
    this.children = [];
  }
}

2、创造树

树用来连接节点,就像真实世界树的主干一样,延伸着很多分支

class MultiwayTree {
  constructor() {
    this._root = null;
  }
}

3、添加一个节点

function add(data, toData, traversal) {
  let node = new Node(data)
  // 第一次添加到根节点
  // 返回值为this,便于链式添加节点
  if (this._root === null) {
    this._root = node;
    return this;
  }
  let parent = null,
    callback = function(node) {
      if (node.data === toData) {
        parent = node;
        return true;
      }
    };
  // 根据遍历方法查找父节点(遍历方法后面会讲到),然后把节点添加到父节点
  // 的children数组里
  // 查找方法contains后面会讲到
  this.contains(callback, traversal);
  if (parent) {
    parent.children.push(node);
    node.parent = parent;
    return this;
  } else {
    throw new Error('Cannot add node to a non-existent parent.');
  }
}

4、深度优先遍历

深度优先会尽量先从子节点查找,子节点查找完再从兄弟节点查找,适合数据深度比较大的情况,如文件目录

function traverseDF(callback) {
  let stack = [], found = false;
  stack.unshift(this._root);
  let currentNode = stack.shift();
  while(!found && currentNode) {
    // 根据回调函数返回值决定是否在找到第一个后继续查找
    found = callback(currentNode) === true ? true : false;
    if (!found) {
      // 每次把子节点置于堆栈最前头,下次查找就会先查找子节点
      stack.unshift(...currentNode.children);
      currentNode = stack.shift();
    }
  }
}

5、广度优先遍历

广度优先遍历会优先查找兄弟节点,一层层往下找,适合子项较多情况,如公司岗位级别

function traverseBF(callback) {
  let queue = [], found = false;
  queue.push(this._root);
  let currentNode = queue.shift();
  while(!found && currentNode) {
    // 根据回调函数返回值决定是否在找到第一个后继续查找
    found = callback(currentNode) === true ? true : false;
    if (!found) {
      // 每次把子节点置于队列最后,下次查找就会先查找兄弟节点
      queue.push(...currentNode.children)
      currentNode = queue.shift();
    }
  }
}

6、包含节点

function contains(callback, traversal) {
  traversal.call(this, callback);
}

回调函数算法可自己根据情况实现,灵活度较高

7、移除节点

// 返回被移除的节点
function remove(data, fromData, traversal) {
  let parent = null,
    childToRemove = null,
    callback = function(node) {
      if (node.data === fromData) {
        parent = node;
        return true;
      }
    };
  this.contains(callback, traversal);
  if (parent) {
    let index = this._findIndex(parent.children, data);
    if (index < 0) {
      throw new Error('Node to remove does not exist.');
    } else {
      childToRemove = parent.children.splice(index, 1);
    }
  } else {
    throw new Error('Parent does not exist.');
  }
  return childToRemove;
}

_findIndex实现:

function _findIndex(arr, data) {
  let index = -1;
  for (let i = 0, len = arr.length; i < len; i++) {
    if (arr[i].data === data) {
      index = i;
      break;
    }
  }
  return index;
}

完整算法

class Node {
  constructor(data) {
    this.data = data;
    this.parent = null;
    this.children = [];
  }
}
class MultiwayTree {
  constructor() {
    this._root = null;
  }
  //深度优先遍历
  traverseDF(callback) {
    let stack = [], found = false;
    stack.unshift(this._root);
    let currentNode = stack.shift();
    while(!found && currentNode) {
      found = callback(currentNode) === true ? true : false;
      if (!found) {
        stack.unshift(...currentNode.children);
        currentNode = stack.shift();
      }
    }
  }
  //广度优先遍历
  traverseBF(callback) {
    let queue = [], found = false;
    queue.push(this._root);
    let currentNode = queue.shift();
    while(!found && currentNode) {
      found = callback(currentNode) === true ? true : false;
      if (!found) {
        queue.push(...currentNode.children)
        currentNode = queue.shift();
      }
    }
  }
  contains(callback, traversal) {
    traversal.call(this, callback);
  }
  add(data, toData, traversal) {
    let node = new Node(data)
    if (this._root === null) {
      this._root = node;
      return this;
    }
    let parent = null,
      callback = function(node) {
        if (node.data === toData) {
          parent = node;
          return true;
        }
      };
    this.contains(callback, traversal);
    if (parent) {
      parent.children.push(node);
      node.parent = parent;
      return this;
    } else {
      throw new Error('Cannot add node to a non-existent parent.');
    }
  }
  remove(data, fromData, traversal) {
    let parent = null,
      childToRemove = null,
      callback = function(node) {
        if (node.data === fromData) {
          parent = node;
          return true;
        }
      };
    this.contains(callback, traversal);
    if (parent) {
      let index = this._findIndex(parent.children, data);
      if (index < 0) {
        throw new Error('Node to remove does not exist.');
      } else {
        childToRemove = parent.children.splice(index, 1);
      }
    } else {
      throw new Error('Parent does not exist.');
    }
    return childToRemove;
  }
  _findIndex(arr, data) {
    let index = -1;
    for (let i = 0, len = arr.length; i < len; i++) {
      if (arr[i].data === data) {
        index = i;
        break;
      }
    }
    return index;
  }
}

控制台测试代码

var tree = new MultiwayTree();
tree.add('a')
  .add('b', 'a', tree.traverseBF)
  .add('c', 'a', tree.traverseBF)
  .add('d', 'a', tree.traverseBF)
  .add('e', 'b', tree.traverseBF)
  .add('f', 'b', tree.traverseBF)
  .add('g', 'c', tree.traverseBF)
  .add('h', 'c', tree.traverseBF)
  .add('i', 'd', tree.traverseBF);
console.group('traverseDF');
tree.traverseDF(function(node) {
  console.log(node.data);
});
console.groupEnd('traverseDF');
console.group('traverseBF');
tree.traverseBF(function(node) {
  console.log(node.data);
});
console.groupEnd('traverseBF');
// 深度优先查找
console.group('contains1');
tree.contains(function(node) {
  console.log(node.data);
  if (node.data === 'f') {
    return true;
  }
}, tree.traverseDF);
console.groupEnd('contains1')
// 广度优先查找
console.group('contains2');
tree.contains(function(node) {
  console.log(node.data);
  if (node.data === 'f') {
    return true;
  }
}, tree.traverseBF);
console.groupEnd('contains2');
tree.remove('g', 'c', tree.traverseBF);

这里使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试运行效果如下:

javascript数据结构之多叉树经典操作示例【创建、添加、遍历、移除等】

感兴趣的朋友可以自己测试一下看看运行效果。

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
Javascript 日期对象Date扩展方法
May 30 Javascript
不使用中间变量,交换int型的 a, b两个变量的值。
Oct 29 Javascript
能说明你的Javascript技术很烂的五个原因分析
Oct 28 Javascript
页面只能打开一次Cooike如何实现
Dec 04 Javascript
jQuery实现鼠标划过修改样式的方法
Apr 14 Javascript
轻松5句话解决JavaScript的作用域
Jul 15 Javascript
php 修改密码实现代码
May 24 Javascript
Javascript实现一个简单的输入关键字添加标签效果实例
Jun 01 Javascript
JQuery扩展对象方法操作示例
Aug 21 jQuery
angular使用md5,CryptoJS des加密的方法
Jun 03 Javascript
微信小程序移动拖拽视图-movable-view实例详解
Aug 17 Javascript
Vue 自适应高度表格的实现方法
May 13 Javascript
JavaScript事件冒泡与事件捕获实例分析
Aug 01 #Javascript
JS+HTML实现的圆形可点击区域示例【3种方法】
Aug 01 #Javascript
create-react-app 修改为多入口编译的方法
Aug 01 #Javascript
Vue项目全局配置页面缓存之按需读取缓存的实现详解
Aug 01 #Javascript
JavaScript执行环境及作用域链实例分析
Aug 01 #Javascript
Vue.js 利用v-for中的index值实现隔行变色
Aug 01 #Javascript
echarts设置图例颜色和地图底色的方法实例
Aug 01 #Javascript
You might like
模板引擎Smarty深入浅出介绍
2006/12/06 PHP
一个简单的php MVC留言本实例代码(必看篇)
2016/09/22 PHP
php生成无限栏目树
2017/03/16 PHP
google jQuery 引用文件,jQuery 引用地址集合(jquery 1.2.6至jquery1.5.2)
2011/04/24 Javascript
30分钟就入门的正则表达式基础教程
2013/02/25 Javascript
一行命令搞定node.js 版本升级
2014/07/20 Javascript
vue.js的vue-cli脚手架中使用百度地图API的实例
2019/01/21 Javascript
vue实现路由切换改变title功能
2019/05/28 Javascript
判断JavaScript中的两个变量是否相等的操作符
2019/12/21 Javascript
JavaScript对象原型链原理解析
2020/01/22 Javascript
通过JS判断网页是否为手机打开
2020/10/28 Javascript
python中的装饰器详解
2015/04/13 Python
Python计算三维矢量幅度的方法
2015/06/15 Python
Python的Django框架中forms表单类的使用方法详解
2016/06/21 Python
python奇偶行分开存储实现代码
2018/03/19 Python
python装饰器-限制函数调用次数的方法(10s调用一次)
2018/04/21 Python
python 反向输出字符串的方法
2018/07/16 Python
对DJango视图(views)和模版(templates)的使用详解
2019/07/17 Python
Django MEDIA的配置及用法详解
2019/07/25 Python
使用 Python 清理收藏夹里已失效的网站
2019/12/03 Python
keras处理欠拟合和过拟合的实例讲解
2020/05/25 Python
python 输入字符串生成所有有效的IP地址(LeetCode 93号题)
2020/10/15 Python
Python Pandas list列表数据列拆分成多行的方法实现
2020/12/14 Python
pycharm配置python 设置pip安装源为豆瓣源
2021/02/05 Python
伦敦剧院门票:London Theatre Direct
2018/11/21 全球购物
定制别致的瑜伽垫:Sugarmat
2019/06/21 全球购物
如何处理简单的PHP错误
2015/10/14 面试题
简述你对Statement,PreparedStatement,CallableStatement的理解
2013/03/25 面试题
经典c++面试题三
2015/07/08 面试题
求职简历中个人的自我评价
2013/12/25 职场文书
集团薪酬管理制度
2014/01/13 职场文书
关于环保的建议书
2014/05/12 职场文书
公务员个人总结
2015/02/12 职场文书
求职自我评价范文
2015/03/09 职场文书
走进毛泽东观后感
2015/06/04 职场文书
快消品行业营销模式与盈利模式分享
2019/09/27 职场文书