基础概念
普通二叉树:
1)每个结点最多有两颗子树,所以二叉树中不存在度大于2的结点。
2)左子树和右子树是有顺序的,次序不能任意颠倒。
3)即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。
满二叉树:
1)叶子只能出现在最下一层。出现在其它层就不可能达成平衡。
2)非叶子结点的度一定是2。
3)在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。
二叉树的存储结构---二叉链表:
二叉树遍历:
二叉树的遍历是指从二叉树的根结点出发,按照某种次序依次访问二叉树中的所有结点,使得每个结点被访问一次,且仅被访问一次。
二叉树的访问次序可以分为四种:
先序遍历
中序遍历
后序遍历
先序遍历:根据根->左->右的顺序便利整个二叉树,每个节点都遵照根->左->右这个顺序规则执行。比如先序遍历上图的二叉树,遍历访问的顺序为:ABDHIEJCFG
中序遍历:根据左->根->右的顺序便利整个二叉树,每个节点都遵照左->根->右这个顺序规则执行。比如中序遍历上图的二叉树,遍历访问的顺序为:HDIBEJAFCG
后序遍历:根据左->右->根的顺序便利整个二叉树,每个节点都遵照左->右->根这个顺序规则执行。比如后序遍历上图的二叉树,遍历访问的顺序为:HIDJEBFGCA
二叉搜索树
二叉搜索树(Binary Search Tree),又名二叉排序树(Binary Sort Tree)。
下图是一棵二叉搜索树:
(1)若左子树不为空,则左子树上所有节点的值均小于或等于它的根节点的值。
(2)若右子树不为空,则右子树上所有节点的值均大于或等于它的根节点的值。
(3)左、右子树也分别为二叉搜索树。
二叉搜索树数据结构的组织方式:
然后,我们需要实现一些方法:
- insert(key): 向树中插入一个新的节点,key表示插入的节点的值(健)
- search(key): 在书中查找一个键,如何节点存在,则返回true;如果不存在,则返回false
- inOrderTraverse:通过中序遍历方式遍历所有节点
- preOrderTraverse:通过先序遍历方式遍历所有节点
- postOrderTraverse:通过后序遍历方式遍历所有节点
- min: 返回树中最小的键
- max:返回树中最大的键
- remove(key): 从树中移除某个键
我们用BinarySearchTree类来表示二叉搜索树,下面是类的完整代码:
/**
* 二叉搜索树
*/
function BinarySearchTree() {
// 根节点
var root = null
// 节点的类名,key:键值;left:左子树的指针;right:右子树的指针
var Node = function(key) {
this.key = key
this.left = null
this.right = null
}
var insertNode = function(node, newNode) {
if(node.key > newNode.key) {
if(node.left === null) {
node.left = newNode
} else {
insertNode(node.left, newNode)
}
} else {
if(node.right === null) {
node.right = newNode
} else {
insertNode(node.right, newNode)
}
}
}
var inOrderTraverseNode = function(node, callback) {
if(node.left) {
inOrderTraverseNode(node.left, callback)
}
callback && callback(node.key)
if(node.right) {
inOrderTraverseNode(node.right, callback)
}
}
var preOrderTraverseNode = function(node, callback) {
callback && callback(node.key)
if(node.left) {
preOrderTraverseNode(node.left, callback)
}
if(node.right) {
preOrderTraverseNode(node.right, callback)
}
}
var postOrderTraverseNode = function(node, callback) {
if(node.left) {
postOrderTraverseNode(node.left, callback)
}
if(node.right) {
postOrderTraverseNode(node.right, callback)
}
callback(node.key)
}
var searchNode = function(node, key) {
if(key === node.key) {
return true
}
if(key < node.key && node.left) {
return searchNode(node.left, key)
}
if(key > node.key && node.right) {
return searchNode(node.right, key)
}
return false
}
var findMinNode = function(node) {
while(node.left) {
node = node.left
}
return node
}
var removeNode = function(node, key) {
if(key < node.key && node.left) {
node.left = removeNode(node.left, key)
return node
}
if(key > node.key && node.right) {
node.right = removeNode(node.right, key)
return node
}
if(key === node.key) {
// 情况一:删除的节点是叶子节点
if(node.left === null && node.right === null) {
node = null
return node
}
// 情况二:删除的节点度为1
if(node.left === null) {
node = node.right
return node
}
if(node.right === null) {
node = node.left
return node
}
// 情况三:删除的节点度为2
if(node.left && node.right) {
var rMinNode = findMinNode(node.right)
node.key = rMinNode.key
node.right = removeNode(node.right, rMinNode.key)
return node
}
}
}
this.insert = function(key) {
var newNode = new Node(key)
if(root === null) {
root = newNode
} else {
insertNode(root, newNode)
}
}
this.inOrderTraverse = function(callback) {
root && inOrderTraverseNode(root, callback)
}
this.preOrderTraverse = function(callback) {
root && preOrderTraverseNode(root, callback)
}
this.postOrderTraverse = function(callback) {
root && postOrderTraverseNode(root, callback)
}
this.min = function() {
if(!root) {
return
}
var node = root
while(node.left) {
node = node.left
}
return node.key
}
this.max = function() {
if(!root) {
return
}
var node = root
while(node.right) {
node = node.right
}
return node.key
}
this.search = function(key) {
if(!root) {
return false
}
return searchNode(root, key)
}
this.remove = function(key) {
root && (root = removeNode(root, key))
}
}
下面构造生成上图中的二叉搜索树,并执行遍历/移除等操作:
var binarySearchTree = new BinarySearchTree() // 实例化一个二叉搜索树,目前没有一个节点
// 通过插入新的节点生成一棵又节点的二叉搜索树
binarySearchTree.insert(17)
binarySearchTree.insert(5)
binarySearchTree.insert(35)
binarySearchTree.insert(2)
binarySearchTree.insert(11)
binarySearchTree.insert(9)
binarySearchTree.insert(8)
binarySearchTree.insert(29)
binarySearchTree.insert(38)
// 中序遍历,遍历的顺序正好是所有键从小到大排序的结果
console.log(`%c 中序遍历:`, `color: #ff0000`)
binarySearchTree.inOrderTraverse(function(key) {
console.log(key)
})
// 先序遍历
console.log(`%c 先序遍历:`, `color: #ff0000`)
binarySearchTree.preOrderTraverse(function(key) {
console.log(key)
})
// 后序遍历
console.log(`%c 后序遍历:`, `color: #ff0000`)
binarySearchTree.postOrderTraverse(function(key) {
console.log(key)
})
// 查找一个键
console.log(`%c 查找一个键:`, `color: #ff0000`)
console.log(binarySearchTree.search(35))
// 最小键
console.log(`%c 最小键:`, `color: #ff0000`)
console.log(binarySearchTree.min())
// 最大键
console.log(`%c 最大键:`, `color: #ff0000`)
console.log(binarySearchTree.max())
// 移除某个键
console.log(`%c 移除某个键后:`, `color: #ff0000`)
binarySearchTree.remove(5)
二叉搜索树更多知识
最坏的情况下,一棵二叉搜索树所有节点可能都只有左节点或右节点