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 相关文章推荐
用js判断浏览器是否是IE的比较好的办法
May 08 Javascript
Javascript面象对象成员、共享成员变量实验
Nov 19 Javascript
kmock javascript 单元测试代码
Feb 06 Javascript
JS判断页面加载状态以及添加遮罩和缓冲动画的代码
Oct 11 Javascript
js onclick事件传参讲解
Nov 06 Javascript
jQuery使用removeClass方法删除元素指定Class的方法
Mar 26 Javascript
jQuery Validate验证框架详解(推荐)
Dec 17 Javascript
jquery拼接ajax 的json和字符串拼接的方法
Mar 11 Javascript
jQuery遍历节点方法汇总(推荐)
May 13 jQuery
微信小程序 同步请求授权的详解
Aug 04 Javascript
解决微信小程序中的滚动穿透问题
Sep 16 Javascript
微信小程序实现组件顶端固定或底端固定效果(不随滚动而滚动)
Apr 09 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
PHP源代码数组统计count分析
2011/08/02 PHP
PHP学习笔记之字符串编码的转换和判断
2014/05/22 PHP
php基于curl扩展制作跨平台的restfule 接口
2015/05/11 PHP
jQuery ajax 路由和过滤器使用说明
2011/08/02 Javascript
script的async属性以非阻塞的模式加载脚本
2013/01/15 Javascript
仿百度的关键词匹配搜索示例
2013/09/25 Javascript
基于JQuery实现滚动到页面底端时自动加载更多信息
2014/01/31 Javascript
基于jquery的文字向上跑动类似跑马灯的效果
2014/09/22 Javascript
javascript实现倒计时N秒后网页自动跳转代码
2014/12/11 Javascript
jQuery插件slides实现无缝轮播图特效
2015/04/17 Javascript
JavaScript与jQuery实现的闪烁输入效果
2016/02/18 Javascript
JavaScript基础教程——入门必看篇
2016/05/20 Javascript
js 声明数组和向数组中添加对象变量的简单实例
2016/07/28 Javascript
AngularJS遍历获取数组元素的方法示例
2017/11/11 Javascript
webpack 开发和生产并行设置的方法
2018/11/08 Javascript
Node.js + express实现上传大文件的方法分析【图片、文本文件】
2019/03/14 Javascript
vue实现多条件和模糊搜索功能
2019/05/28 Javascript
Vue配置marked链接添加target=&quot;_blank&quot;的方法
2019/07/19 Javascript
js 判断当前时间是否处于某个一个时间段内
2019/09/19 Javascript
JS面向对象编程——ES6 中class的继承用法详解
2020/03/03 Javascript
原生JS实现相邻月份日历
2020/10/13 Javascript
最近Python有点火? 给你7个学习它的理由!
2017/06/26 Python
Python实现的栈(Stack)
2018/01/26 Python
Python修改文件往指定行插入内容的实例
2019/01/30 Python
Python eval的常见错误封装及利用原理详解
2019/03/26 Python
使用python的turtle函数绘制一个滑稽表情
2020/02/28 Python
de Bijenkorf比利时官网:荷兰最知名的百货商店
2017/06/29 全球购物
JMS中Topic和Queue有什么区别
2013/05/15 面试题
计算机毕业大学生推荐信
2013/12/01 职场文书
创建精神文明单位实施方案
2014/03/08 职场文书
积极向上的团队口号
2014/06/06 职场文书
学院党委班子四风问题自查报告及整改措施
2014/10/25 职场文书
2019邀请函格式及范文
2019/05/20 职场文书
Python实现归一化算法详情
2022/03/18 Python
gtx1650怎么样 gtx1650显卡相当于什么级别
2022/04/08 数码科技
mysql sock 文件解析及作用讲解
2022/07/15 MySQL