二叉樹(Binary Tree)是 n(n >= 0)個節點的有限集合,集合爲空集時,叫做空二叉樹;不爲空時,由根節點及左子樹、右子樹組成,左子樹、右子樹也都是二叉樹。javascript
從這個描述,能夠看出樹的結構與遞歸之間存在密切關係,這種密切關係在樹的遍歷時可以獲得充分體現。java
二叉搜索樹(Binary Search Tree),又叫二叉查找樹;也稱爲有序二叉樹(Ordered Binary Tree),排序二叉樹(Sorted Binary Tree)。node
這是維基百科上概括的一些二叉搜索樹的性質:算法
本次實現中有點不同的地方,右節點是大於或等於父節點的。不過對示例沒有影響,並且很容易改爲只能大於父節點。數據結構
當年看過這本書的初版,最近打算複習一下數據結構與算法,因而看了第二版。post
這是可貴的一本用 JavaScript 實現的數據結構與算法的書,它的講解十分清晰,相對來講質量較高,但問題也有不少。應該說初版的內容仍是比較可靠的,第二版新增的內容可靠性就差了不少。總的來講,這是一本從很是淺顯的層面講解數據結構與算法的書,想得到比較全面的知識仍是須要閱讀更專業的資料。學習
本文的 ES5 實現參考了這本書,由於我以爲它是比較工整的實現,用於學習和理解時,好過我看到的其餘一些實現方式。在原書示例代碼的基礎上,我作了一些小調整。本書第二版號稱擁抱 ES6,但我看過以後發現至少樹這一章沒有改成 ES6 的實現,因而本身寫了一遍,正好看成練習的機會。ui
另外,這本書中提到的樹的遍歷方式包括中序、先序、後序遍歷,這些都屬於深度優先遍歷。在本文的代碼中,我補充了廣度優先遍歷以及按照層次遍歷二叉樹的實現。this
var BinarySearchTree = function() {
var Node = function(key) {
this.key = key;
this.left = null;
this.right = null;
};
var root = null;
var insertNode = function(node, newNode) {
if (newNode.key < node.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, cb) {
if (node !== null) {
inOrderTraverseNode(node.left, cb);
cb(node.key);
inOrderTraverseNode(node.right, cb);
}
};
var preOrderTraverseNode = function(node, cb) {
if (node !== null) {
cb(node.key);
preOrderTraverseNode(node.left, cb);
preOrderTraverseNode(node.right, cb);
}
};
var postOrderTraverseNode = function(node, cb) {
if (node !== null) {
postOrderTraverseNode(node.left, cb);
postOrderTraverseNode
}
};
var levelOrderTraverseNode = function(node, cb) {
if (node === null) {
return null;
}
var list = [node];
while (list.length > 0) {
node = list.shift();
cb(node.key);
if (node.left) {
list.push(node.left);
}
if (node.right) {
list.push(node.right);
}
}
};
var separateByLevelFn = function(node, cb, separator) {
var list = [];
var END_FLAG = 'END_FLAG';
list.push(node);
list.push(END_FLAG);
separator = separator || '---*---';
while (list.length > 0) {
node = list.shift();
// 遇到結束信號,表示已經遍歷完一層;若隊列中還有元素,說明它們是剛剛遍歷完的這一層的全部子元素。
if (node === END_FLAG && list.length > 0) {
list.push(END_FLAG);
cb(separator);
} else {
cb(node.key);
if (node.left) {
list.push(node.left)
}
if (node.right) {
list.push(node.right);
}
}
}
};
var minNode = function(node) {
if (node) {
while (node.left !== null) {
node = node.left;
}
return node.key;
}
return null;
};
var maxNode = function(node) {
if (node) {
while (node.right !== null) {
node = node.right;
}
return node.key;
}
return null;
};
var searchNode = function(node, val) {
if (node === null) {
return false;
}
if (val < node.key) {
return searchNode(node.left, val);
} else if (val > node.key) {
return searchNode(node.right, val);
} else {
return true;
}
};
var findMinNode = function(node) {
if (node) {
while (node.left !== null) {
node = node.left;
}
return node;
}
return null;
};
var removeNode = function(node, key) {
if (node === null) {
return null;
}
if (key < node.key) {
node.left = removeNode(node.left, key);
return node;
} else if (key > node.key) {
node.right = removeNode(node.right, key);
return node;
} else {
if (node.left === null && node.right === null) {
node = null;
return node;
}
if (node.left === null) {
node = node.right;
return node;
}
if (node.right === null) {
node = node.left;
return node;
}
var aux = findMinNode(node.right);
node.key = aux.key;
node.right = removeNode(node.right, aux.key);
return node;
}
};
this.insert = function(key) {
var newNode = new Node(key);
if (root === null) {
root = newNode;
} else {
insertNode(root, newNode);
}
};
// 中序遍歷是一種以上行順序訪問BST全部節點的遍歷方式,也就是以從最小到最大的順序訪
// 問全部節點。中序遍歷的一種應用就是對樹進行排序操做。
this.inOrderTraverse = function(cb) {
inOrderTraverseNode(root, cb);
};
// 先序遍歷是以優先於後代節點的順序訪問每一個節點的。先序遍歷的一種應用是打印一個結構化的文檔。
this.preOrderTraverse = function(cb) {
preOrderTraverseNode(root, cb);
};
// 後序遍歷則是先訪問節點的後代節點,再訪問節點自己。後序遍歷的一種應用是計算一個目
// 錄和它的子目錄中全部文件所佔空間的大小。
this.postOrderTraverse = function(cb) {
postOrderTraverseNode(root, cb);
};
// Breadth-First-Search
// 能夠用來解決尋路徑的問題。
this.levelOrderTraverse = function(cb) {
levelOrderTraverseNode(root, cb);
}
// Breadth-First-Search
// 區分層次
this.separateByLevel = function(cb) {
separateByLevelFn(root, cb);
}
this.min = function() {
return minNode(root);
};
this.max = function() {
return maxNode(root);
};
this.search = function(val) {
searchNode(root, val);
};
this.remove = function(key) {
root = removeNode(root, key);
};
};
/* ========== test case ========== */
var tree = new BinarySearchTree();
/** * * 11 * / \ * / \ * / \ * / \ * / \ * / \ * 7 15 * / \ / \ * / \ / \ * 5 9 13 20 * / \ / \ / \ / \ * 3 6 8 10 12 14 18 25 * */
tree.insert(11);
tree.insert(7);
tree.insert(15);
tree.insert(5);
tree.insert(3);
tree.insert(9);
tree.insert(8);
tree.insert(10);
tree.insert(13);
tree.insert(12);
tree.insert(14);
tree.insert(20);
tree.insert(18);
tree.insert(25);
tree.insert(6);
var printNode = function(val) {
console.log(val);
};
tree.inOrderTraverse(printNode);
console.log('\n')
tree.levelOrderTraverse(printNode);
console.log('\n')
tree.separateByLevel(printNode);
console.log('\n')
tree.remove(7)
tree.inOrderTraverse(printNode);
console.log('\n')
tree.preOrderTraverse(printNode);
console.log('\n')
tree.postOrderTraverse(printNode);
複製代碼
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
}
}
// insert(key):向樹中插入一個新的鍵。
// search(key):在樹中查找一個鍵,若是節點存在,則返回true;若是不存在,則返回false。
// inOrderTraverse:經過中序遍歷方式遍歷全部節點。
// preOrderTraverse:經過先序遍歷方式遍歷全部節點。
// postOrderTraverse:經過後序遍歷方式遍歷全部節點。
// min:返回樹中最小的值/鍵。
// max:返回樹中最大的值/鍵。
// remove(key):從樹中移除某個鍵。
class BinarySearchTree {
constructor() {
this.root = null;
}
static insertNode(node, newNode) {
if (node.key > newNode.key) {
if (node.left === null) {
node.left = newNode;
} else {
BinarySearchTree.insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
BinarySearchTree.insertNode(node.right, newNode);
}
}
}
static searchNode(node, key) {
if (node === null) {
return false;
}
if (node.key === key) {
return true;
} else if (node.key > key) {
BinarySearchTree.searchNode(node.left, key);
} else if (node.key < key) {
BinarySearchTree.searchNode(node.right, key);
}
}
static inOrderTraverseNode(node, cb) {
if (node === null) {
return;
}
BinarySearchTree.inOrderTraverseNode(node.left, cb);
cb(node.key);
BinarySearchTree.inOrderTraverseNode(node.right, cb);
}
static preOrderTraverseNode(node, cb) {
if (node === null) {
return;
}
cb(node.key);
BinarySearchTree.preOrderTraverseNode(node.left, cb);
BinarySearchTree.preOrderTraverseNode(node.right, cb);
}
static postOrderTraverseNode(node, cb) {
if (node === null) {
return;
}
BinarySearchTree.postOrderTraverseNode(node.left, cb);
BinarySearchTree.postOrderTraverseNode(node.right, cb);
cb(node.key);
}
static levelOrderTraverseNode(node, cb) {
if (node === null) {
return null;
}
const list = [node];
while (list.length > 0) {
node = list.shift();
cb(node.key);
if (node.left) {
list.push(node.left);
}
if (node.right) {
list.push(node.right);
}
}
}
static separateByLevelFn(node, cb, separator = '---*---') {
const list = [];
const END_FLAG = 'END_FLAG';
list.push(node);
list.push(END_FLAG);
while (list.length > 0) {
node = list.shift();
// 遇到結束信號,表示已經遍歷完一層;若隊列中還有元素,說明它們是剛剛遍歷完的這一層的全部子元素。
if (node === END_FLAG && list.length > 0) {
list.push(END_FLAG);
cb(separator);
} else {
cb(node.key);
if (node.left) {
list.push(node.left)
}
if (node.right) {
list.push(node.right);
}
}
}
}
static removeNode(node, key) {
if (node === null) {
return null;
}
if (node.key === key) {
if (node.left === null && node.right === null) {
node = null;
return node;
} else if (node.left === null) {
node = node.right;
return node;
} else if (node.right === null) {
node = node.left;
return node;
} else if (node.left && node.right) {
let rightMinNode = node.right;
while (rightMinNode.left !== null) {
rightMinNode = rightMinNode.left;
}
node.key = rightMinNode.key;
node.right = BinarySearchTree.removeNode(node.right, rightMinNode.key);
return node;
}
} else if (node.key > key) {
node.left = BinarySearchTree.removeNode(node.left, key);
return node;
} else if (node.key < key) {
node.right = BinarySearchTree.removeNode(node.right, key);
return node;
}
}
static printNode(val) {
console.log(val);
}
insert(key) {
const newNode = new Node(key);
if (this.root === null) {
this.root = newNode;
} else {
BinarySearchTree.insertNode(this.root, newNode);
}
}
search(key) {
return BinarySearchTree.searchNode(key);
}
// 中序遍歷是一種以上行順序訪問BST全部節點的遍歷方式,也就是以從最小到最大的順序訪
// 問全部節點。中序遍歷的一種應用就是對樹進行排序操做。
inOrderTraverse(cb = BinarySearchTree.printNode) {
BinarySearchTree.inOrderTraverseNode(this.root, cb);
}
// 先序遍歷是以優先於後代節點的順序訪問每一個節點的。先序遍歷的一種應用是打印一個結構化的文檔。
preOrderTraverse(cb = BinarySearchTree.printNode) {
BinarySearchTree.preOrderTraverseNode(this.root, cb);
}
// 後序遍歷則是先訪問節點的後代節點,再訪問節點自己。後序遍歷的一種應用是計算一個目
// 錄和它的子目錄中全部文件所佔空間的大小。
postOrderTraverse(cb = BinarySearchTree.printNode) {
BinarySearchTree.postOrderTraverseNode(this.root, cb);
}
// Breadth-First-Search
// 能夠用來解決尋路徑的問題。
levelOrderTraverse(cb = BinarySearchTree.printNode) {
BinarySearchTree.levelOrderTraverseNode(this.root, cb);
}
// Breadth-First-Search
// 區分層次
separateByLevel(cb = BinarySearchTree.printNode) {
BinarySearchTree.separateByLevelFn(this.root, cb);
}
min() {
let node = this.root;
if (node === null) {
return null;
}
while (node.left !== null) {
node = node.left;
}
return node.key;
}
max() {
let node = this.root;
if (node === null) {
return null;
}
while (node.right !== null) {
node = node.right;
}
return node.key();
}
remove(key) {
this.root = BinarySearchTree.removeNode(this.root, key);
}
}
/* ========== test case ========== */
const tree = new BinarySearchTree();
/** * * 11 * / \ * / \ * / \ * / \ * / \ * / \ * 7 15 * / \ / \ * / \ / \ * 5 9 13 20 * / \ / \ / \ / \ * 3 6 8 10 12 14 18 25 * */
tree.insert(11);
tree.insert(7);
tree.insert(15);
tree.insert(5);
tree.insert(3);
tree.insert(9);
tree.insert(8);
tree.insert(10);
tree.insert(13);
tree.insert(12);
tree.insert(14);
tree.insert(20);
tree.insert(18);
tree.insert(25);
tree.insert(6);
tree.inOrderTraverse();
console.log('\n')
tree.levelOrderTraverse();
console.log('\n')
tree.separateByLevel();
console.log('\n')
tree.remove(7)
tree.inOrderTraverse();
console.log('\n')
tree.preOrderTraverse();
console.log('\n')
tree.postOrderTraverse();
複製代碼