JavaScript数据结构——树

:非顺序数据结构,对于存储需要快速查找的数据非常有用。
二叉树:二叉树中的节点最多只能有两个子节点(左侧子节点和右侧子节点)。这些定义有助于我们写出更高效的向/从树中插入、查找和删除节点的算法。
二叉搜索树:二叉树的一种,但是它只允许你在左侧节点存储(比父节点)小的值,在右侧节点存储(比父节点)大/等于的值。
 
遍历一棵树:是指访问树的每个节点并对它们进行某种操作的过程。访问树的所有节点有三种方式:中序、先序和后序。
中序遍历:是一种以上行顺序访问 BST 所有节点的的遍历方式,也就是以从最小到最大的顺序访问所有节点。中序遍历的一种应用就是对树进行排序操作。
先序遍历:是以优先于后代节点的顺序访问每个节点的。先序遍历的一种应用是打印一个结构化的文档。
后序遍历:是先访问节点的后代节点,再访问节点本身。后序遍历的一种应用是计算一个目录和它的子目录中所有文件所占空间的大小。
搜索树中的值:在树中,有三种经常执行的搜索类型:最小值、最大值、搜索特定的值
 
BST 存在一个问题:取决于添加的节点树,树的一条边可能会非常深;也就是说,树的一条分支会有很多层,二其他的分支却只有几层。这会在需要在某条边上添加、移除和搜索某个节点时引起一些性能问题。
AVL树:为了解决 BST 的问题,有一种树叫作阿德尔森-维尔斯和兰迪斯树(AVL树)。AVL树是一种自平衡二叉搜索树,意思是任何一个节点两侧子树的高度之差最多为1。也就是说这种树会在添加或移除节点时尽量试着成为一棵完全树。
红黑树:特殊的二叉树,可以进行高效的中序遍历。
 
术语:
         术语             
                                  解释                                 
节点
树中的每个元素
根节点
树顶部的节点,没有父节点
内部节点
至少有一个子节点的节点
外部节点(叶节点)
没有子元素的节点
子树
由节点和它的后代构成
深度
节点的一个属性,取决于它的祖先节点的数量
高度
取决于所有节点深度的最大值

---------------------------------------------------------------------------------------------------------
二叉搜索树方法声明:
1. 和链表一样,通过指针来表示节点之间的关系(术语称其为边);
2. 声明 Node 来表示树中的每个节点。
注意:不同于之前将节点本身称作节点或项,我们会称其为。键是树相关的术语中对节点的称呼。
序号
方法
说明
1
insert(key) 向树中插入一个新的键
2
search(key) 在树中查找一个键,如果节点存在,则返回 true;如果不存在,则返回 false
3
inOrderTraverse 通过中序遍历方式遍历所有节点
4
preOrderTraverse 通过先序遍历方式遍历所有节点
5
postOrderTraverse 通过后序遍历方式遍历所有节点
6
min
返回树种最小的值/键
7
max 返回树种最大的值/键
8
remove(key) 从树中移除某个键

二叉搜索树的实现:
  1 function BinarySearchTree() {
  2     // 辅助类Node定义
  3     var Node = function(key) {
  4         this.key = key;
  5         this.left = null;
  6         this.right = null;
  7     }
  8     var root = null;
  9 
 10     // 向树中插入一个新的键
 11     this.insert = function(key) {
 12         var newNode = new Node(key);
 13 
 14         if (root === null) {
 15             root = newNode;
 16         } else {
 17             insertNode(root, newNode);    // 私有的辅助函数
 18         }
 19     }
 20 
 21     this.insertNode = function(node, newNode) {
 22         if (newNode.key < node.key) {
 23             if (node.left === null) {
 24                 node.left = newNode;
 25             } else {
 26                 insertNode(node.left, newNode);    // 如果有左侧子节点,则通过递归调用继续找到树的下一层
 27             }
 28         } else {
 29             if (node.right === null) {
 30                 node.right = newNode;
 31             } else {
 32                 insertNode(node.right, newNode); // 如果有右侧子节点,则通过递归调用继续找到树的下一层
 33             }
 34         }
 35     }
 36 
 37     /* 中序遍历
 38      * inOrderTraverse方法接收一个回调函数作为参数。
 39      * 回调函数用来定义我们对遍历到的每个节点进行的操作(也叫访问者模式)
 40     */
 41     this.inOrderTraverse = function(callback) {
 42         inOrderTraverseNode(root, callback);
 43     }
 44 
 45     this.inOrderTraverseNode = function(node, callback) {
 46         if (node !== null) {
 47             inOrderTraverseNode(node.left, callback);
 48             callback(node.key);
 49             inOrderTraverseNode(node.right, callback);
 50         }
 51     }
 52 
 53     // 先序遍历
 54     this.preOrderTraverse = function(callback) {
 55         preOrderTraverseNode(root, callback);
 56     }
 57 
 58     this.preOrderTraverseNode = function(node, callback) {
 59         if (node !== null) {
 60             callback(node.key);
 61             preOrderTraverseNode(node.left, callback);
 62             preOrderTraverseNode(node.right, callback);
 63         }
 64     }
 65 
 66     // 后序遍历
 67     this.postOrderTraverse = function(allback) {
 68         postOrderTraverseNode(root, callback);
 69     }
 70 
 71     this.postOrderTraverseNode = function(node, callback) {
 72         if (node !== null) {
 73             postOrderTraverseNode(node.left, callback);
 74             postOrderTraverseNode(node.right, callback);
 75             callback(node.key);
 76         }
 77     }
 78 
 79     // 搜索树中的最小值
 80     this.min = function() {
 81         return minNode(root);
 82     }
 83 
 84     var minNode = function(node) {
 85         if (node) {
 86             while (node && node.left !== null) {
 87                 node = node.lfet;
 88             }
 89             return node.key;
 90         }
 91         return null;
 92     }
 93 
 94     // 搜索树中的最大值
 95     this.max = function() {
 96         return maxNode(root);
 97     }
 98 
 99     this.maxNode = function(node) {
100         if (node) {
101             while (node && node.right !== null) {
102                 node = node.right;
103             }
104             return node.key;
105         }
106         return null;
107     }
108 
109     // 搜索树中的特定值
110     this.search = function(key) {
111         return searchNode(root, key);
112     }
113 
114     var searchNode = function(node, key) {
115         if (node === null) {
116             return false;
117         }
118         if (key < node.key) {
119             return searchNode(node.left ,key);
120         } else if (key > node.key) {
121             return searchNode(node.right, key);
122         } else {
123             return true;
124         }
125     }
126 
127     // 从树中移除某个键
128     this.remove = function(key) {
129         root = removeNode(root, key);    // root被赋值为removeNode方法的返回值
130     }
131 
132     var removeNode = function(node, key) {
133         if (node === null) {    // 键不存在于树中,则返回null
134             return null;
135         }
136         if (key < node.key) {    // 如果要找的键比当前节点的值小,就沿着树的左边找到下一个节点
137             node.left = removeNode(node.left, key);
138             return node;
139         } else if (key > node.key) { // 如果要找的键比当前节点的值大,就沿着树的左边找到下一个节点
140             node.right = removeNode(node.right, key);
141             return node;
142         } else {    // 键等于node.key
143 
144             // 第一种情况——一个叶节点
145             if (node.left === null && node.right === null) {
146                 node = null;
147                 return node;
148             }
149 
150             // 第二种情况——一个只有一个子节点的节点
151             if (node.left === null) {
152                 node = node.right;
153                 return node;
154             } else if (node.right === null) {
155                 node = node.left;
156                 return node;
157             }
158 
159             // 第三种情况——一个有两个子节点的节点
160             var aux = findMinNode(node.right);    
161             node.key = aux.key;    // 用右侧子树中的最小节点的键去更新这个节点的值
162             node.right = removeNode(node.right, aux.key);    // 移除右侧子树中的最小节点
163             return node;
164         }
165     }
166 }
BinarySearchTree.js
 
 参考书籍:《学习JavaScript数据结构与算法》 
 

posted on 2016-03-28 11:22  Ruth92  阅读(330)  评论(0编辑  收藏  举报

导航