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访问XML数据的实例
Dec 27 Javascript
一段实现页面上的图片延时加载的js代码
Feb 11 Javascript
js获取浏览器基本信息大全
Nov 27 Javascript
node.js中的http.response.getHeader方法使用说明
Dec 14 Javascript
Vue.js实现无限加载与分页功能开发
Nov 03 Javascript
jQuery实现倒计时(倒计时年月日可自己输入)
Dec 02 Javascript
微信小程序侧边栏滑动特效(左右滑动)
Jan 23 Javascript
解决vue-router在同一个路由下切换,取不到变化的路由参数问题
Sep 01 Javascript
JS返回页面时自动回滚到历史浏览位置
Sep 26 Javascript
解决vue无法设置滚动位置的问题
Oct 07 Javascript
node.js使用net模块创建服务器和客户端示例【基于TCP协议】
Feb 14 Javascript
Vue是怎么渲染template内的标签内容的
Jun 05 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笔试题
2009/08/04 PHP
Laravel框架实现利用中间件进行操作日志记录功能
2018/06/06 PHP
jQuery 图像裁剪插件Jcrop的简单使用
2009/05/22 Javascript
再谈javascript 动态添加样式规则 W3C校检
2009/12/25 Javascript
jquery鼠标停止移动事件
2013/12/21 Javascript
IE与FireFox的JavaScript兼容问题解决办法
2013/12/31 Javascript
jQuery focus和blur事件的应用详解
2014/01/26 Javascript
原生js实现淘宝首页点击按钮缓慢回到顶部效果
2014/04/06 Javascript
js定时调用方法成功后并停止调用示例
2014/04/08 Javascript
js事件绑定快捷键以ctrl+k为例
2014/09/30 Javascript
浅谈javascript的call()、apply()、bind()的用法
2016/02/21 Javascript
Bootstrap树形菜单插件TreeView.js使用方法详解
2016/11/01 Javascript
jquery心形点赞关注效果的简单实现
2016/11/14 Javascript
JS实现本地存储信息的方法(基于localStorage与userData)
2017/02/18 Javascript
Angular排序实例详解
2017/06/28 Javascript
关于vue-resource报错450的解决方案
2017/07/24 Javascript
基于Vue实现支持按周切换的日历
2020/09/24 Javascript
JS获取一个表单字段中多条数据并转化为json格式
2017/10/17 Javascript
js 判断当前时间是否处于某个一个时间段内
2019/09/19 Javascript
vuex actions异步修改状态的实例详解
2019/11/06 Javascript
vue element自定义表单验证请求后端接口验证
2019/12/11 Javascript
原生JS实现微信通讯录
2020/06/18 Javascript
JS图片懒加载技术实现过程解析
2020/07/27 Javascript
javascript开发实现贪吃蛇游戏
2020/07/31 Javascript
jQuery中event.target和this的区别详解
2020/08/13 jQuery
彻底搞懂并解决vue-cli4中图片显示的问题实现
2020/08/31 Javascript
Python中文件遍历的两种方法
2014/06/16 Python
Flask解决跨域的问题示例代码
2018/02/12 Python
详解python uiautomator2 watcher的使用方法
2019/09/09 Python
TensorFlow2.0使用keras训练模型的实现
2021/02/20 Python
信息管理应届生求职信
2014/03/07 职场文书
党支部承诺书范文
2014/03/28 职场文书
金融系应届毕业生求职信
2014/05/26 职场文书
开展党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
2015暑期社会实践通讯稿
2015/07/18 职场文书
Spring中的使用@Async异步调用方法
2021/11/01 Java/Android