孔乙己说茴香豆的茴有四种写法, 这篇文章要讲二叉搜索树的三种写法
二叉搜索树可高效查找, 时间复杂度是O(logn)
每个节点的元素必须是可比较的
abstract class Comparable<T> {
abstract compareTo(e: Comparable<T>): number
}
每个节点都有当前元素和左右子节点
class Node<T> {
public e: T
public left: Node<T> | null
public right: Node<T> | null
constructor(e: T) {
this.e = e
this.left = null
this.right = null
}
}
使用递归实现二叉搜索树
class BST<T extends Comparable<T>> {
private root: Node<T> | null
private _size: number
constructor() {
this.root = null
this._size = 0
}
public get size(): number {
return this._size
}
public get isEmpty(): boolean {
return this._size === 0
}
public add(e: T): void {
if (this.root === null) {
this.root = new Node(e)
++this._size
} else {
this._addHelper(this.root, e)
}
}
private _addHelper(curRoot: Node<T>, e: T) {
if (e.compareTo(curRoot.e) < 0) {
if (curRoot.left === null) {
curRoot.left = new Node(e)
++this._size
} else {
this._addHelper(curRoot.left, e)
}
} else if (e.compareTo(curRoot.e) > 0) {
if (curRoot.right === null) {
curRoot.right = new Node(e)
++this._size
} else {
this._addHelper(curRoot.right, e)
}
}
}
}
优化递归实现二叉搜索树
上面写法里冗余的判断节点是否为空, 下面的写法可以优雅的规避
class BST<T extends Comparable<T>> {
private root: Node<T> | null
private _size: number
constructor() {
this.root = null
this._size = 0
}
public get size(): number {
return this._size
}
public get isEmpty(): boolean {
return this._size === 0
}
public add(e: T): void {
this.root = this._addHelper(this.root, e)
}
private _addHelper(curRoot: Node<T> | null, e: T): Node<T> | null {
if (curRoot === null) {
curRoot = new Node(e)
++this._size
} else {
if (e.compareTo(curRoot.e) < 0) {
curRoot.left = this._addHelper(curRoot.left, e)
} else if (e.compareTo(curRoot.e) > 0) {
curRoot.right = this._addHelper(curRoot.right, e)
}
}
return curRoot
}
}
使用循环实现二叉搜索树
递归是有额外的内存开销的, 下面的写法用循环来添加元素
class BST<T extends Comparable<T>> {
private root: Node<T> | null
private _size: number
constructor() {
this.root = null
this._size = 0
}
public get size(): number {
return this._size
}
public get isEmpty(): boolean {
return this._size === 0
}
public add(e: T): void {
if (this.root === null) {
this.root = new Node(e)
++this._size
} else {
let currentNode = this.root
while (currentNode) {
if (e.compareTo(currentNode.e) < 0) {
if (currentNode.left === null) {
currentNode.left = new Node(e)
++this._size
break
} else {
currentNode = currentNode.left
}
} else if (e.compareTo(currentNode.e) > 0) {
if (currentNode.right === null) {
currentNode.right = new Node(e)
++this._size
break
} else {
currentNode = currentNode.right
}
}
}
}
}
}
测试用例
const bst = new BST()
class TestNode<T> implements Comparable<T> {
public value: number
constructor(val: number) {
this.value = val
}
compareTo(e: TestNode<T>): number {
return this.value - e.value
}
}
;[5, 2, 6, 7, 1, 9, 4, 3, 8, 10]
.map((item) => {
return new TestNode(item)
})
.forEach((item) => {
bst.add(item)
})
console.warn('bst', JSON.stringify(bst, null, 4))
// 输出
bst {
"root": {
"e": {
"value": 5
},
"left": {
"e": {
"value": 2
},
"left": {
"e": {
"value": 1
},
"left": null,
"right": null
},
"right": {
"e": {
"value": 4
},
"left": {
"e": {
"value": 3
},
"left": null,
"right": null
},
"right": null
}
},
"right": {
"e": {
"value": 6
},
"left": null,
"right": {
"e": {
"value": 7
},
"left": null,
"right": {
"e": {
"value": 9
},
"left": {
"e": {
"value": 8
},
"left": null,
"right": null
},
"right": {
"e": {
"value": 10
},
"left": null,
"right": null
}
}
}
}
},
"_size": 10
}