自我测试
本篇文章的测试用例及调试方法见前言
图
树的相关术语
根节点: 位于树顶部的节点叫作根节点
内部节点: 至少有一个子节点的节点称为内部节点(7、5、9、15、13和20是内部节点)
外部节点(叶节点):没有子元素的节点称为外部节点或叶节点(3、6、8、10、12、14、18和25是叶节点)
子树:由节点和它的后代构成。例如,节点13、12和14构成了上图中树的一棵子树
深度:节点的深度取决于它的祖先节点的数量。比如,节点3有3个祖先节点(5、7和11),它的深度为3
高度:树的高度取决于所有节点深度的最大值。一棵树也可以被分解成层级。根节点在第0层,它的子节点在第1层,以此类推。上图中的树的高度为3(最大高度已在图中表示——第3层)
一个节点可以有祖先和后代。一个节点(除了根节点)的祖先包括父节点、祖父节点、曾祖父节点等。一个节点的后代包括子节点、孙子节点、曾孙节点等。例如,节点5的祖先有节点7和节点11,后代有节点3和节点6。
二叉树和二叉搜索树
二叉树中的节点最多只能有两个子节点:一个是左侧子节点,另一个是右侧子节点。这些定义有助于我们写出更高效的向/从树中插入、查找和删除节点的算法。二叉树在计算机科学中的应用非常广泛。
二叉搜索树(BST)是二叉树的一种,但是它只允许你在左侧节点存储(比父节点)小的值,在右侧节点存储(比父节点)大(或者等于)的值。上图中就展现了一棵二叉搜索树。
图例
基础方法
insert(key): 向二叉搜索树插入一个新的键
search(key): 在树中搜索一个键.如果存在该节点,就返回true;不存在就返回false
inOrderTraverse():通过中序遍历的方式遍历所有节点
preOrderTraverse():通过先序遍历的方式遍历所有节点
postOrderTraverse():通过后序遍历的方式遍历所有的节点
min():返回树中最小的值/键
max():返回树中最大的值/键
remove(key):从树中移除某个键
基础了解
root:表示根节点,在链表中使用head
节点:这里的树节点会有三个属性
left:表示其左侧节点
key:存放当前节点数据
right: 表示其右侧节点
代码实现
enum Compare {
LESS_THAN = -1,
BIGGER_THAN = 1,
EQUALS = 0
}
class TreeNode<T> {
key: T;
left: TreeNode<T> | undefined;
right: TreeNode<T> | undefined;
constructor(key: T) {
this.key = key;
this.left = undefined;
this.right = undefined;
}
}
function defaultCompareFn<T>(parent: TreeNode<T>, child: TreeNode<T>) {
if (parent === child) {
return Compare.EQUALS
}
return parent > child ? Compare.BIGGER_THAN : Compare.LESS_THAN;
}
export class BinarySearchTree<T> {
root: TreeNode<T> | undefined;
compareFn: Function;
count: number;
constructor(compareFn: Function = defaultCompareFn) {
this.root = undefined;
this.count = 0;
this.compareFn = compareFn;
}
//向二叉搜索树插入一个新的键
insert(key: T) {
// 当root没有值的时候
if (this.root == null) {
this.root = new TreeNode<T>(key);
} else {
// 当root有值时,判断当前key比root大(right)还是小(left)
return this.insertTreeNode(this.root, key);
}
}
//节点插入
insertTreeNode(cNode: TreeNode<T>, key: T) {
// 新值小于node
if (this.compareFn(cNode.key, key) === Compare.BIGGER_THAN) {
// left节点为null
if (cNode.left == null) {
cNode.left = new TreeNode<T>(key);
} else {
//left节点不为null就继续在左节点插入
this.insertTreeNode(cNode.left, key)
}
}
// 新值大于node right节点为null
else {
if (cNode.right == null) {
cNode.right = new TreeNode<T>(key);
} else {
// 新值没有插入又没有跳过就是插入right
this.insertTreeNode(cNode.right, key);
}
}
}
// 搜索节点值
search(key: T) {
this.searchTreeNode(this.root, key);
}
// 搜索某个节点
searchTreeNode(node: TreeNode<T> | null, key: T): Boolean {
//如果node没有值,则返回false,表示没有找到
if (node == null) {
return false;
}
switch (this.compareFn(node.key, key)) {
//父节点等于子节点
case Compare.EQUALS:
return true;
break;
// 父节点小于当前数据 所以要在right节点上找
case Compare.LESS_THAN:
return this.searchTreeNode(node.right, key)
break;
// 父节点大于当前数据 所以应该在左节点上找
default:
return this.searchTreeNode(node.left, key)
break;
}
}
//通过中序遍历的方式遍历所有节点
inOrderTraverse(callBack: Function) {
this.inOrderTraverseNode(this.root, callBack);
}
inOrderTraverseNode(node: TreeNode<T> | null, callBack: Function) {
if (node == null) {
return;
}
this.inOrderTraverseNode(node.left, callBack);
callBack(node.key);
this.inOrderTraverseNode(node.right, callBack);
}
//通过先序遍历的方式遍历所有节点
preOrderTraverse(callBack: Function) {
this.preOrderTraverseNode(this.root, callBack);
}
preOrderTraverseNode(node: TreeNode<T> | null, callBack: Function) {
if (node == null) {
return;
}
callBack(node.key);
this.inOrderTraverseNode(node.left, callBack);
this.inOrderTraverseNode(node.right, callBack);
}
//通过后序遍历的方式遍历所有的节点
postOrderTraverse(callBack: Function) {
this.postOrderTraverseNode(this.root, callBack);
}
postOrderTraverseNode(node: TreeNode<T> | null, callBack: Function) {
if (node == null) {
return;
}
this.inOrderTraverseNode(node.left, callBack);
this.inOrderTraverseNode(node.right, callBack);
callBack(node.key);
}
//返回树中最小的值/键
min(): TreeNode<T> {
if (this.root == null) {
return undefined;
}
if (this.root.left == null) {
return this.root;
}
return this.minNode(this.root);
}
minNode(node: TreeNode<T> | null): TreeNode<T> {
if (node) {
if (node.left == null) {
return node;
} else {
return this.minNode(node.left);
}
}
return undefined;
}
//返回树中最大的值/键
max(): TreeNode<T> {
if (this.root == null) {
return undefined;
}
if (this.root.right == null) {
return this.root;
}
return this.maxNode(this.root);
}
maxNode(node: TreeNode<T> | null): TreeNode<T> {
if (node) {
if (node.right == null) {
return node;
} else {
return this.maxNode(node.right);
}
} else {
return undefined;
}
}
getRoot() {
return this.root;
}
//移除一个节点
remove(key: T) {
this.root = this.removeNode(this.root, key);
}
removeNode(node: TreeNode<T>, key: T): TreeNode<T> {
//判断这个树上是否有节点
if (node == undefined) {
return undefined;
}
//树上是有节点的,这个时候就要判断当前节点是否与删除的元素相同
switch (this.compareFn(node.key, key)) {
//父节点等于子节点
case Compare.EQUALS:
node = this.removeCurrentNode(node);
return node;
break;
// 父节点小于当前数据 所以要在right节点上找
case Compare.LESS_THAN:
node.right = this.removeNode(node.right, key)
return node
break;
// 父节点大于当前数据 所以应该在左节点上找
default:
node.left = this.removeNode(node.left, key)
return node
break;
}
}
removeCurrentNode(node: TreeNode<T>): TreeNode<T> {
//分成三种情况
//一:叶结点(没有左右子节点),那么我们直接将该节点赋值为undefined
if (node.left == null && node.right == null) {
node = undefined;
return node;
}
//二:只有一个左节点或一个右节点
//左节点不存在,将父节点的右节点指针指向移除节点的右节点
if (node.left == null) {
node = node.right;
return node;
} else if(node.right == null){
node = node.left;
return node;
}
//三:有两个节点的元素
const aux = this.minNode(node.right);
node.key = aux.key;
node.right = this.removeNode(node.right,aux.key);
return node;
}
}
辅助分析:
递归遍历部分图解分析(这里只分析了左半部分):
从下向上看"调用栈"
从下向上看"调用栈"
从上向下看"调用栈"
书中代码
export enum Compare {
LESS_THAN = -1,
BIGGER_THAN = 1,
EQUALS = 0
}
type ICompareFunction<T> = (a: T, b: T) => number;
function defaultCompare<T>(a: T, b: T): number {
if (a === b) {
return Compare.EQUALS;
}
return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN;
}
class Node<K> {
left: Node<K>;
right: Node<K>;
constructor(public key: K) {}
toString() {
return `${this.key}`;
}
}
export default class BinarySearchTree<T> {
protected root: Node<T>;
constructor(protected compareFn: ICompareFunction<T> = defaultCompare) {}
insert(key: T) {
// special case: first key
if (this.root == null) {
this.root = new Node(key);
} else {
this.insertNode(this.root, key);
}
}
protected insertNode(node: Node<T>, key: T) {
if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
if (node.left == null) {
node.left = new Node(key);
} else {
this.insertNode(node.left, key);
}
} else if (node.right == null) {
node.right = new Node(key);
} else {
this.insertNode(node.right, key);
}
}
getRoot() {
return this.root;
}
search(key: T) {
return this.searchNode(this.root, key);
}
private searchNode(node: Node<T>, key: T): boolean {
if (node == null) {
return false;
}
if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
return this.searchNode(node.left, key);
} else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) {
return this.searchNode(node.right, key);
}
// key is equal to node.item
return true;
}
inOrderTraverse(callback: Function) {
this.inOrderTraverseNode(this.root, callback);
}
private inOrderTraverseNode(node: Node<T>, callback: Function) {
if (node != null) {
this.inOrderTraverseNode(node.left, callback);
callback(node.key);
this.inOrderTraverseNode(node.right, callback);
}
}
preOrderTraverse(callback: Function) {
this.preOrderTraverseNode(this.root, callback);
}
private preOrderTraverseNode(node: Node<T>, callback: Function) {
if (node != null) {
callback(node.key);
this.preOrderTraverseNode(node.left, callback);
this.preOrderTraverseNode(node.right, callback);
}
}
postOrderTraverse(callback: Function) {
this.postOrderTraverseNode(this.root, callback);
}
private postOrderTraverseNode(node: Node<T>, callback: Function) {
if (node != null) {
this.postOrderTraverseNode(node.left, callback);
this.postOrderTraverseNode(node.right, callback);
callback(node.key);
}
}
min() {
return this.minNode(this.root);
}
protected minNode(node: Node<T>) {
let current = node;
while (current != null && current.left != null) {
current = current.left;
}
return current;
}
max() {
return this.maxNode(this.root);
}
protected maxNode(node: Node<T>) {
let current = node;
while (current != null && current.right != null) {
current = current.right;
}
return current;
}
remove(key: T) {
this.root = this.removeNode(this.root, key);
}
protected removeNode(node: Node<T>, key: T) {
if (node == null) {
return null;
}
if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
node.left = this.removeNode(node.left, key);
return node;
} else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) {
node.right = this.removeNode(node.right, key);
return node;
} else {
// key is equal to node.item
// handle 3 special conditions
// 1 - a leaf node
// 2 - a node with only 1 child
// 3 - a node with 2 children
// case 1
if (node.left == null && node.right == null) {
node = null;
return node;
}
// case 2
if (node.left == null) {
node = node.right;
return node;
} else if (node.right == null) {
node = node.left;
return node;
}
// case 3
const aux = this.minNode(node.right);
node.key = aux.key;
node.right = this.removeNode(node.right, aux.key);
return node;
}
}
}