繼續前面系列,這一章主要分析二叉樹和二叉查找樹。 樹的定義: node
正是由於二叉樹的這些特性,讓它的查找效率極高,從而引出了二叉查找樹,左子節點保存相對父節點較小的值,右節點保存相對父節點較大的值算法
接下來,咱們來一塊兒實現這個二叉查找樹。 咱們要定義一個對象Node來表示這棵樹:數組
function Node(data, left, right) {
this.data = data;
this.left = left;
this.right = right;
this.show = show;
}
function show() {
return this.data;
}
複製代碼
這個Node對象能夠保存數據,也保存和其餘節點的連接。 如今,能夠建立一個類,用來表示二叉查找樹,簡稱BST。bash
function BST() {
this.root = null;
this.insert = insert;
this.inOrder = inOrder;
}
function insert(data) {
var n = new Node(data, null, null);
if (this.root == null) {
this.root = n;
}else {
var current = this.root;
var parent;
while (true) {
parent = current;
if (data < current.data) {
current = current.left;
if (current == null) {
parent.left = n;
break;
}
}else {
current = current.right;
if (current == null) {
parent.right = n;
break;
}
}
}
}
}
複製代碼
這個BST的insert方法有點複雜,咱們來分析下都作了那些事:數據結構
(1)首先建立一個沒有左右子節點的Node實例;post
(2)若是這是第一個插入BST的節點就做爲根節點;ui
(3)不是的話,獲取這個根節點賦值給current變量往下走this
(4)進入一個循環判斷,找到正確的插入點會跳出循環spa
(5)若是待插入節點保存的數據小於當前節點,則設新的當前爲原節點的左節點code
(6)若是當前節點的左節點爲null,就將新的節點插入這個位置,退出循環,反之,繼續往下循環查找。
(7)查找右節點也相似。
有三種遍歷BST的方法:分別是中序,先序和後序,其中中序遍歷最爲常見。
有了這個思路咱們就能夠寫出這個中序遍歷的方法:
function inOrder(node) {
if (!(node == null)) {
inOrder(node.left);
putstr(node.show() + " ");
inOrder(node.right);
}
}
複製代碼
這裏用到了遞歸的思想,該方法須要以升序訪問樹中全部節點,因此它首先會遞歸查找到最下面的左葉子節點,而後訪問左子樹,再訪問根節點,最後訪問右子樹。
preOrder(node) {
if (!(node == null)) {
putstr(node.show() + " ");
preOrder(node.left);
preOrder(node.right);
}
}
複製代碼
注意:中序遍歷和先序遍歷的惟一區別,就是if語句中的代碼順序。
function postOrder(node){
if(!(node == null)){
postOrder(node.left);
postOrder(node.right);
putstr(node.show() + " ")
}
}
複製代碼
由於較小的值老是在左子節點,在BST上查找最小值,只要遍歷左子樹,直到找到最後一個節點:
function getMin(){
let current = this.root;
while(!(current.left == null)){
current = current.left;
}
return current.data
}
複製代碼
查找最大值只要遍歷右子樹直到找到最後一個節點便可。
查找給定值,稍微麻煩點,咱們須要將給定值和當前節點進行比較,來決定是左遍歷仍是右遍歷。
function find(data){
let current = this.root;
while(current !=null){
if(current.data == data){
return current
}
else if(data<current.data){
current = current.left
}else{
current = current.right
}
}
return null;
}
複製代碼
若是找到給定值,返回給定值,找不到會返回null
從BST上刪除節點的操做最複雜,由於咱們要考慮幾種狀況:
(1)是否包含待刪除的數據
(2)待刪除節點是不是葉子節點
(3)待刪除節點是否只包含一個子節點
(4)待刪除節點是否包含兩個子節點
爲了方便,咱們將刪除節點拆分紅兩個方法,remove()方法接收待刪除的數據,removeNode()方法刪除節點:
function remove(data){
root = removeNode(this.root, data)
}
function removeNode(node,data){
if(node == null){
return null;
}
if(data == node.data){
//葉子節點
if(node.left == null && node.right == null){
return null
}
//沒有左子節點
if(node.left == null){
return node.right
}
//沒有右節點
if(node.right == null){
return node.left
}
//有兩個子節點
let tempNode = getSmallest(node.right);
node.data = tempNode.data;
node.right = removeNode(node.right, tempNode.data);
return node
}
else if (data < node.data){
node.left = removeNode(node.left, data);
}
else {
node.right = removeNode(node.right, data);
}
}
複製代碼
上面,咱們就完成了二叉查找樹的增刪改查。
咱們以前有說到:
數組 的搜索比較方便,能夠直接用下標,但刪除或者插入某些元素就比較麻煩。 鏈表 與之相反,刪除和插入元素很快,但查找很慢。 二叉查找樹 就既有鏈表的好處,也有數組的好處。 在處理大批量的動態的數據是比較有用。
可是,人無完人,當考慮到二叉查找樹的隨機性,在最壞的狀況下(就是那種一條腿的感受),時間複雜度和順序查找差很少,這就讓人有點沒法接受了。
究其緣由,仍是由於左右子樹相差懸殊致使的,因而有人經過算法研究出了平衡二叉查找樹 ,也就是紅黑樹
就長這樣,這個要講清楚又是一個大東西,博主也沒徹底研究明白,等後續有時間弄清楚了奉賢給你們0.0