树(Tree)
本文作为树数据结构的第三篇,介绍二叉搜索树BST上除了删除方法之外的其它方法的实现思路,这些方法包括:节点插入,先序、中序、后序遍历,获取最大、最小key节点、查找特定key节点。
1. 插入节点
- 插入一个新的节点的算法如下:
- 判断树是否为空,如果是空树,则新插入的节点直接成为根节点
- 如果不是空的,则尝试将新节点作为根节点的子节点插入;如果没有成功,则根节点应该指明该新节点下一步应该尝试插入到哪一个节点
- 尝试插入的过程如下:
- 判断新节点的key的值是否小于尝试性父节点
- 如果小于,则进一步查看该节点的left是否空缺,如空缺则直接插入,否则指导新节点插入到其已有的左子节点中去;
- 如果大于,则进一步查看该节点的right是否空缺,如空缺则直接插入,否则指导新节点插入到其已有的右子节点中去;
- 如果等于,则插入非法,不处理。
- 判断新节点的key的值是否小于尝试性父节点
insert(key: number): void{
// 根据key产生一个新节点
const newNode = new _Node(key);
// 判断根节点是不是存在
if (!this.root) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
insertNode(targetNode: _Node, newNode: _Node): void {
if (newNode.key < targetNode.key) {
if (!targetNode.left) {
targetNode.left = newNode;
} else {
this.insertNode(targetNode.left, newNode);
}
} else {
if (!targetNode.right) {
targetNode.right = newNode;
} else {
this.insertNode(targetNode.right, newNode);
}
}
}
2. 获取树中最大/最小key的节点
算法为:由于BST是有序结构,所以获取最大或者最小节点是比较简单的;事实上,树中最大的key节点就是右下角的节点;而最小key节点就是左下角的节点,因此,只要顺着root一直往左或者右往下走就可以得到最小值或者最大值了:
min(): number {
if(!this.root) return NaN;
let pointerNode = this.root;
while(pointerNode.left){
pointerNode = pointerNode.left;
}
return pointerNode.key;
}
max(): number {
if(!this.root) return NaN;
let pointerNode = this.root;
while(pointerNode.right){
pointerNode = pointerNode.right;
}
return pointerNode.key;
}
3. 根据节点的key搜索对应的节点
算法为:从根节点开始, 将key和当前节点的key比较,依据比此节点key大的都在其右边,比此节点key小的都在左边的这个特点,通过二分法迅速找到对应节点:
search(key: number): null | _Node {
return this.searchNode(this.root, key);
}
searchNode(node: null | _Node, key: number): null | _Node {
if(!node) return null;
if (node.key > key) {
// 此时应该搜索其左边
return this.searchNode(node.left, key);
} else if (node.key < key) {
// 此时应该搜索其右边
return this.searchNode(node.right, key);
} else {
// 找到的话就直接返回
return node;
}
}
4. 先序遍历
- 首先必须解释清楚的是【先序遍历】中的“先”的含义:先是针对树或者子树的根节点来说的,对一个根节点来说,有它自己、左子节点、右子节点三个对象,先序遍历的含义就是:先遍历自己,再遍历左子树,再遍历右子树:根->左->右;
- 先序遍历的算法:从根节点开始,先遍历根,然后是左子树,最后是右子树
preOrderTraverse(handler: Function): void {
this.preOrderTraverseNode(this.root, handler);
}
preOrderTraverseNode(node: null | _Node, handler: Function): void {
if(node){
// 先访问根节点
handler(node);
// 然后是左子树
this.preOrderTraverseNode(node.left, handler);
// 最后是右子树
this.preOrderTraverseNode(node.right, handler);
}
}
5. 中序遍历
- 有了先序遍历的铺垫,中序遍历中的“中”,表示的无非是根节点要在中间被访问;
- 中序遍历的算法:从根节点开始,先遍历左子树,然后是根节点,最后是右子树
midOrderTraverse(handler: Function): void {
this.midOrderTraverseNode(this.root, handler);
}
midOrderTraverseNode(node: null | _Node, handler: Function): void {
if(node){
// 先访问左子树
this.midOrderTraverseNode(node.left, handler);
// 然后是根节点
handler(node);
// 最后是右子树
this.midOrderTraverseNode(node.right, handler);
}
}
6. 后序遍历
无需多言
postOrderTraverse(handler: Function): void {
this.postOrderTraverseNode(this.root, handler);
}
postOrderTraverseNode(node: null | _Node, handler: Function): void {
if(node){
// 先访问左子树
this.postOrderTraverseNode(node.left, handler);
// 然后是右子树
this.postOrderTraverseNode(node.right, handler);
// 最后是根节点
handler(node);
}
}
注意
先序、中序、后序:说的都是根节点的位置,这三种遍历方式都是从root节点开始的,并且左一定在右之前!
7. 整体代码
class _Node {
left: _Node | null = null;
right: _Node | null = null;
constructor (public key: number) {}
}
class _BST {
root: _Node | null = null;
insert(key: number): void{
// 根据key产生一个新节点
const newNode = new _Node(key);
// 判断根节点是不是存在
if (!this.root) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
insertNode(targetNode: _Node, newNode: _Node): void {
if (newNode.key < targetNode.key) {
if (!targetNode.left) {
targetNode.left = newNode;
} else {
this.insertNode(targetNode.left, newNode);
}
} else {
if (!targetNode.right) {
targetNode.right = newNode;
} else {
this.insertNode(targetNode.right, newNode);
}
}
}
preOrderTraverse(handler: Function): void {
this.preOrderTraverseNode(this.root, handler);
}
preOrderTraverseNode(node: null | _Node, handler: Function): void {
if(node){
// 先访问根节点
handler(node);
// 然后是左子树
this.preOrderTraverseNode(node.left, handler);
// 最后是右子树
this.preOrderTraverseNode(node.right, handler);
}
}
midOrderTraverse(handler: Function): void {
this.midOrderTraverseNode(this.root, handler);
}
midOrderTraverseNode(node: null | _Node, handler: Function): void {
if(node){
// 先访问左子树
this.midOrderTraverseNode(node.left, handler);
// 然后是根节点
handler(node);
// 最后是右子树
this.midOrderTraverseNode(node.right, handler);
}
}
postOrderTraverse(handler: Function): void {
this.postOrderTraverseNode(this.root, handler);
}
postOrderTraverseNode(node: null | _Node, handler: Function): void {
if(node){
// 先访问左子树
this.postOrderTraverseNode(node.left, handler);
// 然后是右子树
this.postOrderTraverseNode(node.right, handler);
// 最后是根节点
handler(node);
}
}
min(): number {
if(!this.root) return NaN;
let pointerNode = this.root;
while(pointerNode.left){
pointerNode = pointerNode.left;
}
return pointerNode.key;
}
max(): number {
if(!this.root) return NaN;
let pointerNode = this.root;
while(pointerNode.right){
pointerNode = pointerNode.right;
}
return pointerNode.key;
}
search(key: number): null | _Node {
return this.searchNode(this.root, key);
}
searchNode(node: null | _Node, key: number): null | _Node {
if(!node) return null;
if (node.key > key) {
// 此时应该搜索其左边
return this.searchNode(node.left, key);
} else if (node.key < key) {
// 此时应该搜索其右边
return this.searchNode(node.right, key);
} else {
// 找到的话就直接返回
return node;
}
}
// 非常难搞
remove(key: number): boolean {return false;}
}