JavaScript树(二) 二叉树搜索

TypeScript方式实现源码

  1 // 二叉树与二叉树搜索
  2 class Node {
  3     key;
  4     left;
  5     right;
  6     constructor(key) {
  7         this.key = key;
  8         this.left = null;
  9         this.right = null;
 10     }
 11 }
 12 class BinarySearchTree {
 13     root = null;
 14     public insert(key) {
 15         let newNode = new Node(key);
 16 
 17         if (this.root === null) {
 18             this.root = newNode;
 19         } else {
 20             this.insertNode(this.root, newNode);
 21         }
 22     }
 23     private insertNode(node, newNode) {
 24         if (newNode.key < node.key) {
 25             if (node.left === null) {
 26                 node.left = newNode;
 27             } else {
 28                 this.insertNode(node.left, newNode);
 29             }
 30         } else {
 31             if (node.right === null) {
 32                 node.right = newNode;
 33             } else {
 34                 this.insertNode(node.right, newNode);
 35             }
 36         }
 37     }
 38     public search(key) {
 39         return this.searchNode(this.root, key);
 40     }
 41     private searchNode(node, key) {
 42         if (node === null) {
 43             return false;
 44         }
 45         if (key < node.key) {
 46             return this.searchNode(node.left, key);
 47         } else if (key > node.key) {
 48             return this.searchNode(node.right, key);
 49         } else {
 50             return true;
 51         }
 52     }
 53     /**
 54      * 中序遍历
 55      */
 56     public inOrderTraverse(callback) {
 57         this.inOrderTraverseNode(this.root, callback);
 58     }
 59     private inOrderTraverseNode(node, callback) {
 60         if (node !== null) {
 61             this.inOrderTraverseNode(node.left, callback);
 62             callback(node.key);
 63             this.inOrderTraverseNode(node.right, callback);
 64         }
 65     }
 66     /**
 67      * 先序遍历
 68      */
 69     public preOrderTraverse(callback) {
 70         this.preOrderTraverseNode(this.root, callback)
 71     }
 72     private preOrderTraverseNode(node, callback) {
 73         if (node !== null) {
 74             callback(node.key);
 75             this.preOrderTraverseNode(node.left, callback);
 76             this.preOrderTraverseNode(node.right, callback);
 77         }
 78     }
 79     /**
 80      * 后序遍历
 81      */
 82     public postOrderTraverse(callback) {
 83         this.postOrderTranverseNode(this.root, callback)
 84     }
 85     private postOrderTranverseNode(node, callback) {
 86         if (node !== null) {
 87             this.postOrderTranverseNode(node.left, callback);
 88             this.postOrderTranverseNode(node.right, callback);
 89             callback(node.key);
 90         }
 91     }
 92     public min() {
 93 
 94     }
 95     private minNode(node) {
 96         if (node) {
 97             while (node && node.left !== null) {
 98                 node = node.left;
 99             }
100             return node.key;
101         }
102         return null;
103     }
104     public max(node) {
105         if (node) {
106             while (node && node.right !== null) {
107                 node = node.right;
108             }
109             return node.key;
110         }
111         return null;
112     }
113     public remove(key) {
114         this.root = this.removeNode(this.root, key);
115     }
116     private removeNode(node, key) {
117         if (node === null) {
118             return null;
119         }
120         if (key < node.key) {
121             node.left = this.removeNode(node.left, key);
122             return node;
123         } else if (key > node.key) {
124             node.right = this.removeNode(node.right, key);
125             return node;
126         } else { // 建等于node.key
127             // 第一种情况——一个叶节点
128             if (node.left === null && node.right === null) {
129                 node = null;
130                 return node;
131             }
132 
133             // 第二种情况——一个只有一个子节点的节点
134             if (node.left === null) {
135                 node = node.right;
136                 return node;
137             } else if (node.right === null) {
138                 node = node.left;
139                 return node;
140             }
141 
142             // 第三种情况——一个只有两个子节点的节点
143             let aux = findMinNode(node.right);
144             node.key = aux.key;
145             node.right = this.removeNode(node.right, aux.key);
146             return node;
147         }
148     }
149 }
150 function printNode(value) {
151     console.log(value);
152 }
153 let tree = new BinarySearchTree();
154 tree.insert(11);
155 tree.insert(7);
156 tree.insert(15);
157 tree.insert(5);
158 tree.insert(3);
159 tree.insert(9);
160 tree.insert(8);
161 tree.insert(10);
162 
163 tree.insert(13);
164 tree.insert(12);
165 tree.insert(14);
166 tree.insert(20);
167 tree.insert(18);
168 tree.insert(25);
169 tree.insert(6);
170 
171 // tree.inOrderTraverse(printNode);
172 // tree.preOrderTraverse(printNode);
173 tree.postOrderTraverse(printNode);
174 
175 console.log(tree.search(1) ? 'Key 1 found.' : 'Key 1 not found.');
176 console.log(tree.search(8) ? 'Key 8 found.' : 'Key 8 not found.');
二叉树 BinarySearchTree

二叉树和二叉搜索树 

  二叉树中的节点最多只能有两个子节点:一个是左侧子节点,另一个是右侧子节点。这些定
义有助于我们写出更高效的向/从树中插入、查找和删除节点的算法。二叉树在计算机科学中的
应用非常广泛。
  二叉搜索树(BST)是二叉树的一种,但是它只允许你在左侧节点存储(比父节点)小的值,
在右侧节点存储(比父节点)大(或者等于)的值。上一节的图中就展现了一棵二叉搜索树。
二叉搜索树将是我们在本章中要研究的数据结构。

  下图展现了二叉搜索树数据结构的组织方式:

  

  和链表一样,将通过指针来表示节点之间的关系(术语称其为边) 。在双向链表中,每个节
点包含两个指针,一个指向下一个节点,另一个指向上一个节点。对于树,使用同样的方式(也
使用两个指针) 。但是,一个指向左侧子节点,另一个指向右侧子节点。因此,将声明一个Node
类来表示树中的每个节点(行{1}) 。值得注意的一个小细节是,不同于在之前的章节中将节点
本身称作节点或项,我们将会称其为键。键是树相关的术语中对节点的称呼。
  我们将会遵循和LinkedList类中相同的模式(第5章) ,这表示也将声明一个变量以控制此
数据结构的第一个节点。在树中,它不再是头节点,而是根元素(行{2}) 。
  然后,我们需要实现一些方法。下面是将要在树类中实现的方法。
   insert(key):向树中插入一个新的键。
   search(key):在树中查找一个键,如果节点存在,则返回true;如果不存在,则返回false。 
   inOrderTraverse:通过中序遍历方式遍历所有节点。
   preOrderTraverse:通过先序遍历方式遍历所有节点。

   min:返回树中最小的值/键。
   max:返回树中最大的值/键。
   remove(key):从树中移除某个键。
  我们将在后面的小节中实现每个方法。 

 向树中插入一个键 

  要向树中插入一个新的节点(或项) ,要经历三个步骤。
  第一步是创建用来表示新节点的Node类实例(行{1}) 。只需要向构造函数传递我们想用来
插入树的节点值,它的左指针和右指针的值会由构造函数自动设置为null。
  第二步要验证这个插入操作是否为一种特殊情况。 这个特殊情况就是我们要插入的节点是树
的第一个节点(行{2}) 。如果是,就将根节点指向新节点。
  第三步是将节点加在非根节点的其他位置。

树的遍历 

  遍历一棵树是指访问树的每个节点并对它们进行某种操作的过程。但是我们应该怎么去做
呢?应该从树的顶端还是底端开始呢?从左开始还是从右开始呢?访问树的所有节点有三种方
式:中序、先序和后序。 

中序遍历 

  中序遍历是一种以上行顺序访问BST所有节点的遍历方式, 也就是以从最小到最大的顺序访
问所有节点。中序遍历的一种应用就是对树进行排序操作。

this.inOrderTraverse = function(callback){ 
    inOrderTraverseNode(root, callback); //{1} 
}; 

  inOrderTraverse方法接收一个回调函数作为参数。回调函数用来定义我们对遍历到的每
个节点进行的操作(这也叫作访问者模式,要了解更多关于访问者模式的信息,请参考http://en.
wikipedia.org/wiki/Visitor_pattern) 。由于我们在BST中最常实现的算法是递归,这里使用了一个
私有的辅助函数,来接收一个节点和对应的回调函数作为参数(行{1}) 。

var inOrderTraverseNode = function (node, callback) { 
    if (node !== null) { //{2} 
        inOrderTraverseNode(node.left, callback);  //{3} 
        callback(node.key);                        //{4} 
        inOrderTraverseNode(node.right, callback); //{5} 
    } 
}; 

  要通过中序遍历的方法遍历一棵树,首先要检查以参数形式传入的节点是否为null(这就
是停止递归继续执行的判断条件——行{2}——递归算法的基本条件) 。
  然后,递归调用相同的函数来访问左侧子节点(行{3}) 。接着对这个节点进行一些操作
(callback) ,然后再访问右侧子节点(行{5}) 。
  我们试着在之前展示的树上执行下面的方法:

function printNode(value){ //{6} 
    console.log(value); 
} 
tree.inOrderTraverse(printNode); //{7} 

  但首先,需要创建一个回调函数(行{6}) 。我们要做的,是在浏览器的控制台上输出节点
的值。然后,调用inOrderTraverse方法并将回调函数作为参数传入(行{7}) 。当执行上面的

  代码后,下面的结果将会在控制台上输出(每个数字将会输出在不同的行) :
  3 5 6 7 8 9 10 11 12 13 14 15 18 20 25
  下面的图描绘了inOrderTraverse方法的访问路径:

原文地址:https://www.cnblogs.com/menu/p/6984842.html