這是我參與8月更文挑戰的第8天,活動詳情查看:8月更文挑戰node
(n >= 0)
個節點構成的有限集合(n > 0)
,具有如下性質:
(m > 0)
個互不相交的有限集T一、T二、...、Tm
,其中每一個集合自己又是一棵樹,稱爲原來樹的**"子樹"**樹的性質是不少的...數組
一個二叉樹第i層的最大節點數爲:2^(i-1), i>=1
markdown
好比第一層(根節點)爲1個;第二層最多爲2節點函數
深度爲k的二叉樹有最大節點總數爲:2^k-1, k>=1
post
對任何非空二叉樹T,若n0
表示葉節點(度爲0)的個數,n2
表示度爲2的非葉節點個數,那麼二者知足關係n0 = n2 + 1
ui
除二叉樹最後一層外,其餘各層的節點數都達到最大個數this
且最後一層從左向右達到葉節點連續存在,只缺右側若干節點spa
下面的圖從左到右看,E有右子節點可是D沒有,因此不是徹底二叉樹。若是給D加一個右子節點,那麼它就是徹底二叉樹prototype
二叉樹的存儲方式能夠是數組也能夠是鏈表,可是咱們通常用鏈表。由於使用數組的是否若是該二叉樹不是滿二叉樹,會形成不少空間的浪費。指針
首先,二叉搜索樹有一個根節點,初始化讓這個根節點指向空;this.root = null;
其次,二叉搜索樹的每個節點包含3個元素,左子節點、右子節點和鍵值;初始狀態下指針指向空
function Node() {
this.key = key;
this.left = null;
this.right = null;
}
複製代碼
insert(key):
向樹中插入一個新的鍵search(key):
在樹中查找一個鍵,若是節點存在,則返回true;若是不存在,返回falseinOrderTraverse:
經過中序遍歷方式遍歷全部節點preOrderTraverse:
經過先序遍歷方式遍歷全部節點postOrderTraverse:
經過後序遍歷方式遍歷全部節點min:
返回樹中的最小的值/鍵max:
返回樹中的最大的值/鍵remove(key):
從樹中移除某個鍵insert
方法根據key建立節點
判斷根節點是否有值;若是根節點爲null時直接插入,不然進行下一步操做
BinarySearchTree.prototype.insert = function (key) {
var newNode = new Node(key);
if (this.root == null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
複製代碼
在搜索樹進行插入操做的時候,若是插入的值大於根節點,那麼就要把該節點插到根節點的右子節點;若是此時根節點已經有右子節點了,那就應該繼續讓這個要插入的節點的值和該右子節點進行比較插入;這個過程其實就是在套娃,一直到這個左或者右節點爲空的時候就中止,因此這裏用遞歸來實現,用另外一個函數來進行對節點的插入。
BinarySearchTree.prototype.insertNode = function (node, newNode) {
if (newNode.key < node.key) {
if (node.left == null) {
node.left = newNode;
} else {
this.insertNode(node.left, newNode);
}
} else {
if (node.right == null) {
node.right = newNode;
} else {
this.insertNode(node.right, newNode);
}
}
}
複製代碼
因爲樹的結構是比較特殊的,在遍歷節點的時候咱們每每都是採用遞歸的方法來實現。
BinarySearchTree.prototype.preOrderTraversalNode = function (node, handler) {
if (node != null) {
handler(node.key);
this.preOrderTraversalNode(node.left, handler);
// 3. 處理通過節點的右子節點
this.preOrderTraversalNode(node.right, handler);
}
}
//--------
var resultString = ''
tree.preOrderTraversal(function (key) {
resultString += key + ' ';
})
複製代碼
這裏的handler是一個處理結點的回調函數,便於咱們看遍歷的結果;在先序遍歷中,咱們要先遍歷全部的左子節點,再處理通過節點的右子節點;好比下面這張圖:
這裏用的遞歸思路仍是有一點複雜的,是一個個函數的嵌套,遍歷結束以後一個個跳出來的,須要花一點時間來理解。
遍歷7的左節點到5的時候,先遍歷3,3是葉節點,繼續遍歷6(此時的node是5),遍歷完5的左右節點以後,就繼續遍歷7的右子節點。
if (node != null) {
this.inOrderTraversalNode(node.left, handler);
handler(node.key);
this.inOrderTraversalNode(node.right, handler);
}
複製代碼
if (node != null) {
this.inOrderTraversalNode(node.left, handler);
this.inOrderTraversalNode(node.right, handler);
handler(node.key);
}
複製代碼
理解了先序遍歷以後,中序遍歷和後序遍歷都是用遞歸的方式實現,不一樣之處在於什麼時候來處理節點。
最大值和最小值在數中是很容易找的。最左邊的子節點(左下)就是最小值;最右邊的子節點(右下)就是最大值。這裏演示查找最大值。
BinarySearchTree.prototype.max = function () {
var node = this.root;
var key = null;
while (node != null) {
key = node.key
node = node.right
}
return node.key
}
複製代碼
在查找的時候,一直都找右節點,直到這個右節點的值爲空的時候返回這個節點的鍵值。
這個方法能夠經過遞歸的方法實現,也能夠經過循環來進行搜索
遞歸;將查找值和當前節點的值進行對比,若是較小就往左子樹找,若是較大就往右子樹上查找,直到node.key == key
時,返回true,若是通過這些步驟以後仍是找不到的話就返回false。
BinarySearchTree.prototype.searchNode = function (node, key) {
if (node == null) {
return false;
}
if (node.key > key) {
return this.searchNode(node.left, key);
} else if (node.key < key) {
return this.searchNode(node.right, key);
} else {
return true;
}
return false;
}
複製代碼
循環 先獲取根節點,再循環搜索key
循環的話會比較好理解一點,就是鍵值比較,決定往左子樹仍是右子樹遍歷,直到找到目標值。
var node = this.root;
while (node != null) {
if (node.key > key) {
node = node.left;
} else if (node.key < key) {
node = node.right;
} else {
return true;
}
}
return false;
複製代碼
刪除操做是最複雜的,那就放在下一篇文章吧!今天學不下去了aaa...codewhy老師真的講的好棒!