计算机中的树【附有示例:JS实现的文件管理器】

自然界的树,不需要我来解释。不过对于计算机中的树结构,可难倒了一大片人。其实,我们日常生活中会经常接触到树结构。

比如,一个公司的结构划分。公司最大的当然是老板了,老板将公司划分为多个大部门,各个大部门下又划分为多个小部门,而每个小部门内部还可能会划分为多个小组,组内还会根据工作种类的不同划分为不同的岗位。这里,如果需要查询这个公司总共有多少岗位,你会怎么办?想想都头大啊。

再比如,我们计算机中存储文件的时候,一个文件夹下面有多个子文件夹,而这些子文件夹还会有其他子子文件夹,还可能会有更多。如果我们要找某个文件,在不知道它在哪个文件夹的时候,我们会使用计算机的搜索功能。那么,计算机如何来实现这个功能呢?想想都头大啊。

这篇文章就来介绍一下我用JS实现的树结构及其常用操作。

介绍

了解一点计算机的朋友都知道,树是由一堆具有相同结构的元素嵌套而来的。它根据不同的特性分为很多的种类,比如二叉树,满树,红黑树,B树,B+树等等等。而树的嵌套结构就决定了,它相关的算法多数可以通过递归来解决,比如二叉树的先序遍历、中序遍历等。

废话不多说了,直接来看看实现代码吧。

实现一棵树

一棵树最重要的东西就是它的节点了,包括当前节点及其子节点,另外还需要一个ID来唯一标识每一个节点。所以我这里的节点如下:

var nodeID = 1;

Ycc.Tree = function() {

   /**
    * 节点的自增ID,不允许修改,且每个对象必须唯一
    * @type {number}
    */
   this.$id = nodeID++;

   /**
    * 节点的父节点ID,不允许修改
    * @type {null|Ycc.Tree}
    */
   this.$parentID = null;

   /**
    * 节点的子节点列表
    * @type {Array}
    */
   this.children = [];

   /**
    * 节点所携带的数据
    * @type {any}
    */
   this.data = null;

};

上面代码中Ycc为一个全局变量,是我真正做的一个项目名,可以忽略。

可以看到,这里除了自己的ID外,还使用了parentID,这样在寻找父节点的时候会非常方便。

另外,借助js的灵活性,直接使用children数组表示当前节点的子节点。

如果我告诉你,一棵树我们就写完了,你会感到惊讶吗?

事实确实如此,树是一个集合的概念,集合内元素的结构就决定了树的结构。只是,如果这样我们就需要手动的去设置ID和parentID,以此来保证它们的嵌套。

所以,为了丰富一棵树,我们还需要新增一些便利的方法,来保证树的特性,而不用手动去设置。

给树添加常用的操作

操作一:根据json初始化一棵树

在js及其他语言中,最常用的数据结构莫过于json了。比如,如下结构是示例中的一个json,我们会根据它来生成对应的树。

{
   data:'/',
   children:[
      {
         data:"a",
         children:[
            {
               data:"d",
               children:[
                  {
                     data:"g"
                  },
                  {
                     data:"h"
                  },
                  {
                     data:"i"
                  },
               ]
            },
            {
               data:"e",
               children:[
                  {
                     data:"j"
                  },
                  {
                     data:"k"
                  },
                  {
                     data:"l"
                  },
               ]
            },
            {
               data:"f"
            },
         ]
      },
      {
         data:"b",
         children:[
            {
               data:"m"
            },
            {
               data:"n"
            },
         ]
      },
      {
         data:"c",
         children:[
            {
               data:"o"
            },
            {
               data:"p"
            },
            {
               data:"q"
            },
         ]
      }
   ]
}

这里的实现代码如下:

Ycc.Tree.createByJSON = function (json) {
   var root = new Ycc.Tree();
   root.data = json.data;
   if(Ycc.utils.isArray(json.children) && json.children.length>0){
      for(var i=0;i<json.children.length;i++){
         root.addChildTree(Ycc.Tree.createByJSON(json.children[i]));
      }
   }
   return root;
};

上面代码,第2行,新建了一个root,表示这棵树的根节点,并在第3行将json中的数据附加给节点。

第4行判断节点是否有子节点,如果有子节点,在第5~7行我们递归调用方法createByJSON来为每一个子节点创建一颗子树,并调用addChildTree方法添加到我们的root。

最后返回了我们的树根。

这个addChildTree方法,我们暂时只需知道,它的作用是给当前节点添加一颗子树,稍后给出其实现。

可以看出,这段代码很明显的使用了分治算法策略。

即,将我们的问题分解成多个子问题,子问题使用相同的解决方法(createByJSON),待子问题解决完之后,将子问题合并(addChildTree),即可解决我们的原问题。

操作二:添加一颗子树

Ycc.Tree.prototype.addChildTree = function (tree) {
   if(tree.$parentID) return console.error("sub tree's parent has exist! can't add!",tree);
   tree.$parentID = this.$id;
   this.children.push(tree);
   return this;
};

添加子树,实质上只是添加一个节点ID的引用,即ID和parentID之间的关联关系。

上面代码中,第2行判断了树根,第3~4行就是在操作它们的关联关系。

操作三:获取树的最大深度

树的最大深度是指,从树根开始向下,总共有多少层级。

比如,我们文章开头,对于公司示例,因为有可能有的大部门不划分小部门,这个最大深度是指公司最多划分了几层。

对于文件夹示例,因为有的文件夹可能为空,这个最大深度是指文件夹最多能点到哪儿。

实现代码如下:

Ycc.Tree.prototype.getDepth = function () {
   var self = this;
   var dep = 1;
   if(self.children.length>0){
      for(var i=0;i<self.children.length;i++){
         var subDep = self.children[i].getDepth();
         dep = subDep+1>dep?subDep+1:dep;
      }
   }
   return dep;
};

这个地方也用到了分治策略。

第4行判断有没子级,第5~7行求出子级的最大深度,将其加1与当前最大深度做比较,取最大值即为我们所求的最大深度。

操作四:树的遍历

我总共写了四种遍历方法。分别是:普通遍历 左子树优先遍历 右子树优先遍历 按层级向下遍历。

这里贴一个普通遍历,感兴趣的可以看看其他的遍历方式。

Ycc.Tree.prototype.itor = function (option) {
   var self = this;
   function each(cb) {
      if(cb.call(self,self)) return true;
      if(self.children.length>0){
         for(var i=0;i<self.children.length;i++){
            if(self.children[i].itor().each(cb)) return true;
         }
      }
      return false;
   }
}

地址为:https://github.com/lizhiqianduan/ycc/blob/develop/src/Ycc.Tree.class.js

总结

对于程序员,树结构是经常遇到的,我们的HTML文档就是一棵树,xml文件也是一棵树。

各个省市县也可以构成一颗树,淘宝京东的栏目分类也是一棵树,任何有嵌套结构的数据都可以构成一棵树。

所以,掌握树相关的算法是很有必要的。

最后附一个示例,是我实现的一个模拟windows的文件管理器,点开链接看看吧。

http://www.lizhiqianduan.com/products/ycc/examples/tree/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,711评论 0 13
  • B树的定义 一棵m阶的B树满足下列条件: 树中每个结点至多有m个孩子。 除根结点和叶子结点外,其它每个结点至少有m...
    文档随手记阅读 13,201评论 0 25
  • 1 序 2016年6月25日夜,帝都,天下着大雨,拖着行李箱和同学在校门口照了最后一张合照,搬离寝室打车去了提前租...
    RichardJieChen阅读 5,083评论 0 12
  • Given an array where elements are sorted in ascending ord...
    Eazow阅读 391评论 0 0
  • 我们都知道,自卑也是一种负能量。每个人都想摆脱自卑带来的困扰,我也是因此才学了心理学这门课程。 首先,我们要明确一...
    龙航007阅读 3,191评论 0 1