怎样通过JavaScript遍历树

树基本上只是花哨的链表,并且在树上创建和删除节点非常简单。另一方面,当它们未排序时,搜索会有些棘手,因此我们将研究几种不同的方式来处理整个树的搜索。

先决条件

您将需要基本了解什么是树以及它们如何工作。我们使用Binary Search Tree的特定示例,但是与确切的实现相比,它们更多的是技术和模式,并且可以轻松地适用于任何类型的树。

概念

使用JS二叉搜索树我们可以使用相同的系统来创建一个新节点,就像找到一个一样。标准树(例如文件系统)不遵循任何特定规则,因此迫使我们通过树或子树查看每个项目以查找所需内容。这就是为什么搜索特定文件可能要花费这么长时间的原因。

没有很多方法可以优化过去,O(n)但是有两种主要的“哲学”可以搜索整个树,即通过广度优先(水平在同级之间)或深度优先(垂直在父母与孩子之间)进行搜索。

由于二叉搜索树最容易建立,因此我们可以将仅添加节点的快速树放在一起。

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

class BST {
  constructor() {
    this.root = null;
  };
  create(val) {
    const newNode = new Node(val);
    if (!this.root) {
      this.root = newNode;
      return this;
    };
    let current = this.root;

    const addSide = side => {
      if (!current[side]) {
        current[side] = newNode;
        return this;
      };
      current = current[side];
    };

    while (true) {
      if (val === current.val) return this;
      if (val < current.val) addSide(‘left‘);
      else addSide(‘right‘);
    };
  };
};

const tree = new BST();
tree.create(20);
tree.create(14);
tree.create(57);
tree.create(9);
tree.create(19);
tree.create(31);
tree.create(62);
tree.create(3);
tree.create(11);
tree.create(72);

广度优先搜索

广度优先搜索的特征在于,它着重于从左到右,在每个级别上的每个项目,然后再转移到下一个。

这包括三个主要部分:当前节点,访问的节点列表以及用于跟踪需要查看哪些节点的基本队列(我们将只使用数组,因为它将永远不会很长) 。

无论我们是什么人current,我们都会将其子级(从左到右)推入队列,因此看起来像[20, 14, 57]。然后,我们将切换current到队列中的下一个项目,并将其左,右子级添加到队列的末尾[14, 57, 9, 19]

现在,visited当我们移至下一个项目,查找其子项目并将其添加到队列中时,可以删除并添加当前项目。这将重复进行,直到我们的队列为空并且每个值都在中visited

BFS() {
  let visited = [],
      queue = [],
      current = this.root;

  queue.push(current);
  while (queue.length) {
    current = queue.shift();
    visited.push(current.val);

    if (current.left) queue.push(current.left);
    if (current.right) queue.push(current.right);
  };

    return visited;
}

console.log(tree.BFS()); //[ 20, 14, 57, 9, 19, 31, 62, 3, 11, 72 ]

深度优先搜索

深度优先搜索与完成每个级别相比,更关心的是完成从树的整个侧面到叶子的遍历。

有处理这种方式主要有三种,preOrderpostOrder,和inOrder但他们彼此只是很轻微的修改来改变输出顺序。更好的是,我们甚至不必担心队列。

从根开始,我们将使用一个简短的递归函数来记录我们的节点,然后尽可能向下移动到左边,并记录其路径。完成左侧操作后,它将开始处理剩余的右侧值,直到记录完整个树为止。最终访问应该看起来像[24, 14, 9, 3, 11, 19, ...]

preOrder() {
  let visited = [],
      current = this.root;

  let traverse = node => {
    visited.push(node.val);
    if (node.left) traverse(node.left);
    if (node.right) traverse(node.right);
  };

  traverse(current);
  return visited;
}

console.log(tree.preOrder()); // [ 20, 14, 9, 3, 11, 19, 57, 31, 62, 72 ]

您可能已经猜到了,postOrder与的相反preOrder,我们仍在垂直工作,但是我们从底部到顶部进行搜索,而不是从根到叶。

我们将从最底部的节点开始,并记录其及其兄弟节点,然后再移至其父节点。被访问的前半部分应该看起来像这样[3, 11, 9, 19, 14, ...],因为它可以使它在树上冒泡。

我们可以通过visited在两个遍历完成之后将节点推入来轻松实现此目的。

postOrder() {
  let visited = [],
      current = this.root;

  let traverse = node => {
    if (node.left) traverse(node.left);
    if (node.right) traverse(node.right);
    visited.push(node.val);
  };

  traverse(current);
  return visited;
}

console.log(tree.postOrder()); // [ 3, 11, 9, 19, 14, 31, 72, 62, 57, 20 ]

与的访问类似postOrderpreOrder访问从下而上进行,但仅在访问任何同级之前访问父项。

在遍历左侧之后,在右侧之前,我们可以推入列表,而不是开始或结束。我们的结果将如下所示[3, 9, 11, 14, 19, 20, ...]

inOrder() {
  let visited = [],
      current = this.root;

  let traverse = node => {
    if (node.left) traverse(node.left);
    visited.push(node.val);
    if (node.right) traverse(node.right);
  };

  traverse(current);
  return visited;
}

console.log(tree.inOrder()); // [ 3, 9, 11, 14, 19, 20, 31, 57, 62, 72 ]

总结思想

当然,所有这些算法都是O(n)因为要着眼于每个节点,没有太多的捷径或技巧。

请记住,这些并不是需要记住的确切实现,而是解决问题和构建更有价值的算法的通用模式。一旦您了解了下划线的概念,便可以轻松地将它们适应任何语言或框架。

原文地址:https://blog.51cto.com/14763751/2484642

时间: 2024-08-30 17:25:20

怎样通过JavaScript遍历树的相关文章

JavaScript菜单树

OA系统,ERP系统这样的管理自动化系统.设计者们,总喜欢将菜单放在左侧.菜单太多了,分类折叠是必然的选择.这样既可以有力于菜单模块的管理,也可以方便操作.如下的一个菜单示例: 这样的菜单,我做了一个简单的.自己写了一个TheTree的类,效果还不错,贴上样式,还是杠杠的. 详细的源码可以访问我的github: [email protected]:YeRuGeMiMi/TheTree.git 下面是我的开发过程: 1.生成菜单 我第一步写的菜单是直接硬编码在html中: 1 <ul> 2 &l

【实战】IFE的JavaScript和树

任务目的 熟练JavaScript 学习树这种数据结构的基本知识 任务描述 在页面中展现一颗二叉树的结构 提供一个按钮,显示开始遍历,点击后,以动画的形式呈现遍历的过程 二叉树的遍历算法和方式自定,前序中序后序皆可,但推荐可以提供多种算法的展示(增加多个按钮,每个按钮对应不同的算法) 当前被遍历到的节点做一个特殊显示(比如不同的颜色) 每隔一段时间(500ms,1s等时间自定)再遍历下一个节点 注意事项 如果按照示例图中展示树,可以使用flexbox布局 实现简单功能的同时,请仔细学习JavaS

JavaScript遍历IP段内所有IP

思路:将两个IP转换为数字进行比较,小的那个慢慢加一,直到变成大的那个IP所转换的数字,将这其中的数字再转换为IP地址即为IP段内所有的IP. 1 //IP转数字 2 function ip2int(ip) 3 { 4 var num = 0; 5 ip = ip.split("."); 6 num = Number(ip[0]) * 256 * 256 * 256 + Number(ip[1]) * 256 * 256 + Number(ip[2]) * 256 + Number(i

JQuery+javascript遍历tr td

function InitTable(tableID, trName) { $(tableID + " tr").each(function (index, element) { if (index == 0 || index == $(tableID + " tr").length - 1) { return true; } var s = element.cells[0].innerHTML; if ($.trim(s) != '') { if (s.index

javaScript遍历对象、数组总结

javaScript遍历对象总结 1.使用Object.keys()遍历 var obj = {'0':'a','1':'b','2':'c'}; Object.keys(obj).forEach(function(key){ console.log(key,obj[key]); }); 2.使用for..in..遍历 var obj = {'0':'a','1':'b','2':'c'}; for(var i in obj) { console.log(i,":",obj[i]);

js39---组合模式,查找遍历树

/** *有这样一个需求 *有一个学校有2个班(一班,二班) *每个班级分2个小组(一班一组,一班二组,二班一组,二班二组) *学校计算机教室有限,每一个小组分着来上课. *考试的时候大家一起考 *请用程序来模拟这个需求 */ (function(){ //不用组合模式 //学校类 var school = function(name){ this.name = name; //班级 var classes = new Array(); this.addClasses = function(cl

POJ 1849 Two(遍历树)

http://poj.org/problem?id=1849 题意: 有一颗n个结点的带权的无向树, 在s结点放两个机器人, 这两个机器人会把树的每条边都走一遍, 可是最后机器人不要求回到出发点. 问你两个机器人走的路总长之和的最小值是多少? 分析: 首先本题仅仅要求出树的直径, 然后用树的总长sum*2-树的直径就是所求结果. 以下一步步来说明为什么是这种. 1.如果仅仅有1个机器人遍历树,且要求回到原点, 它最少须要走多少路? 答: 它须要走树总长sum的两倍, 即每条树边它都要走两次才行.

创建先序二叉树-创建层次遍历树

创建先序二叉树 #include<iostream> using namespace std; class BinTreeNode { public:     char ch;     BinTreeNode(int value){ch=value;}     BinTreeNode *left,*right; }; BinTreeNode* create_tree() {     char item;     BinTreeNode *t,*t_l,*t_r;     cin>>

JavaScript遍历对象-总结一

原生JavaScript 遍历 1.for 循环遍历 1 let array1 = ['a','b','c']; 2 3 for (let i = 0;i < array1.length;i++){ 4 console.log(array1[i]); // a b c 5 } 2.JavaScript 提供了 foreach()  map() 两个可遍历 Array对象 的方法 forEach和map用法类似,都可以遍历到数组的每个元素,而且参数一致:   Array.forEach(funct