7-二叉樹(第10章)node
樹是計算機科學中常常用到的一種數據結構。樹是一種非線性的數據結構,以分層的方式存儲數據 。本章將研究一種特殊的樹: 二叉查找樹。算法
7.1 什麼是樹數據結構
樹是一種數據結構,由一組以邊線鏈接的節點組成。如下就是一顆普通的樹函數
樹的節點:一棵樹最上面的節點稱爲根節點,若是一個節點下面鏈接多個節點,那麼該節點稱爲父節點,它下面的節點稱爲子節點。一個節點能夠有 0 個、 1 個或多個子節點。沒有任何子節點的節點稱爲葉子節點。post
樹的遍歷:沿着樹的一組特定的邊,能夠從一個節點走到另一個與它不直接相連的節點。從一個節點到另外一個節點的這一組邊稱爲路徑,在圖中用虛線表示。以某種特定順序訪問樹中全部的節點稱爲樹的遍歷測試
樹的深度:樹能夠分爲幾個層次,根節點是第 0 層,它的子節點是第 1 層,子節點的子節點是第 2層,以此類推。樹中任何一層的節點能夠都看作是子樹的根,該子樹包含根節點的子節點,子節點的子節點等。咱們定義樹的層數就是樹的深度this
7.2 二叉樹spa
首先咱們要了解下什麼是二叉樹,二叉樹是一種特殊的樹,它的子節點個數不超過兩個。二叉查找樹是在二叉樹的基礎上對樹的結構進行了進一步的約束。code
二叉查找樹又名二叉排序樹,有時候也叫二叉搜索樹。它具備如下特徵:對象
7.3 實現一個二叉樹
二叉樹都是由節點組成的,因此咱們要實現二叉樹必需要先實現節點,一個節點一般包含三部分——數據、左子節點、右子節點。所以咱們能夠定義這樣的一個node對象來表明咱們的節點。
class Node {
constructor(data, left, right) {
this.data = data
this.left = left
this.right = right
}
}
複製代碼
Node 對象既保存數據,也保存和其餘節點的連接
如今咱們能夠建立一個類,用來表示二叉查找樹,咱們讓類只包含一個數據成員:一個表示二叉查找樹根節點的 Node 對象。該類的構造函數將根節點初始化爲 null,以此建立一個空節點。
接下來咱們來實現一個insert方法,用來向樹中插入新的節點。實現這個方法的算法以下:
實現二叉樹
class BST {
constructor() {
this.root = null
}
insert(data) {
let n = new Node(data, null, null)
if (this.root === null) {
this.root = n
} else {
var current = this.root
while (true) {
if (n.data < current.data) {
if (current.left === null) {
current.left = n
break
}
current = current.left
} else {
if (current.right === null) {
current.right = n
break
}
current = current.right
}
}
}
}
}
複製代碼
*驗證二叉樹
let bst = new BST()
bst.insert(8)
bst.insert(3)
bst.insert(6)
bst.insert(4)
bst.insert(9)
bst.insert(11)
bst.insert(2)
bst.insert(5)
bst.insert(7)
console.log('bst:',bst)
複製代碼
真實的打印結果很難顯示在博客裏,畫成圖的話以下:
實現是正確的,說明咱們的二叉樹程序沒有問題
7.4 二叉樹的遍歷
二叉樹遍歷經常使用的有如下三種。
遍歷的前中後順序實際上是指訪問根節點的順序
前序遍歷:
// 前序遍歷
preOrder(node) {
if (node != null) {
console.log(node.data)
this.preOrder(node.left)
this.preOrder(node.right)
}
}
複製代碼
中序遍歷:
// 中序遍歷
inOrder(node) {
if (node != null) {
this.inOrder(node.left)
console.log(node.data)
this.inOrder(node.right)
}
}
複製代碼
後序遍歷
// 後序遍歷
postOrder(node) {
if (node !== null) {
this.postOrder(node.left)
this.postOrder(node.right)
console.log(node.data)
}
}
複製代碼
**遍歷的方法使用的是遞歸,很是繞,須要多理解理解
完整代碼以下
class Node {
constructor(data, left, right) {
this.data = data
this.left = left
this.right = right
}
}
class BST {
constructor() {
this.root = null
}
insert(data) {
let n = new Node(data, null, null)
if (this.root === null) {
this.root = n
} else {
var current = this.root
while (true) {
if (n.data < current.data) {
if (current.left === null) {
current.left = n
break
}
current = current.left
} else {
if (current.right === null) {
current.right = n
break
}
current = current.right
}
}
}
}
// 前序遍歷
preOrder(node) {
if (node != null) {
console.log(node.data)
this.preOrder(node.left)
this.preOrder(node.right)
}
}
// 中序遍歷
inOrder(node) {
if (node != null) {
this.inOrder(node.left)
console.log(node.data)
this.inOrder(node.right)
}
}
// 後序遍歷
postOrder(node) {
if (node !== null) {
this.postOrder(node.left)
this.postOrder(node.right)
console.log(node.data)
}
}
}
複製代碼
測試:
let bst = new BST()
bst.insert(8)
bst.insert(3)
bst.insert(6)
bst.insert(4)
bst.insert(9)
bst.insert(11)
bst.insert(2)
bst.insert(5)
bst.insert(7)
bst.preOrder(bst.root) // 8 3 2 6 4 5 7 9 11
bst.inOrder(bst.root) // 2 3 4 5 6 7 8 9 11
bst.postOrder(bst.root) // 2 5 4 7 6 3 11 9 8
複製代碼
測試沒有,說明咱們的遍歷是正確的。
7.5 二叉樹查找
查找給定的值
查找最大值
查找最小值
// 查找最大是
getMax() {
let node = this.root
while (true) {
if (node.right === null) {
console.log(node.data)
break
}
node = node.right
}
}
// 查找最小值
getMin() {
let node = this.root
while (true) {
if (node.left === null) {
console.log(node.data)
break
}
node = node.left
}
}
getSomeNum(num) {
let node = this.root
while (node) {
if (num < node.data) {
node = node.left
} else if (num > node.data) {
node = node.right
} else if (num === node.data) {
console.log('node.data:', node.data)
break
}
}
}
複製代碼
測試
bst.getMax() // 11
bst.getMin() // 2
bst.getSomeNum(9) // node.data: 9
bst.getSomeNum(1) //
複製代碼
7.6 從二叉樹上刪除節點
在二叉樹上刪除節點會比較複雜,若是要刪除的節點沒有子節點還好,若是要刪除的節點有一個節點或者兩個節點,都要考慮到這些節點的處理狀況,這時就比較難以處理。
程序以下:
getSmallest(ndoe) {
while (node.left !== null) {
node = node.left
}
return node
}
remove(data) {
this.root = this.removeNode(this.root, data)
}
// 若是要刪除的節點是當前節點,則返回null,不然返回原節點,並按照規則,遞歸查找下一個節點是否是目標節點
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 = this.getSmallest(node.right)
node.data = tempNode.data
node.right = this.removeNode(node.right, tempNode.data)
return node
} else if (data < node.data) {
node.left = this.removeNode(node.left, data)
return node
} else {
node.right = this.removeNode(node.right, data)
return node
}
}
複製代碼
測試
class Node {
constructor(data, left, right) {
this.data = data
this.left = left
this.right = right
}
}
class BST {
constructor() {
this.root = null
}
insert(data) {
let n = new Node(data, null, null)
if (this.root === null) {
this.root = n
} else {
var current = this.root
while (true) {
if (n.data < current.data) {
if (current.left === null) {
current.left = n
break
}
current = current.left
} else {
if (current.right === null) {
current.right = n
break
}
current = current.right
}
}
}
}
// 前序遍歷
preOrder(node) {
if (node != null) {
console.log(node.data)
this.preOrder(node.left)
this.preOrder(node.right)
}
}
// 中序遍歷
inOrder(node) {
if (node != null) {
this.inOrder(node.left)
console.log(node.data)
this.inOrder(node.right)
}
}
// 後序遍歷
postOrder(node) {
if (node !== null) {
this.postOrder(node.left)
this.postOrder(node.right)
console.log(node.data)
}
}
// 查找最大是
getMax() {
let node = this.root
while (true) {
if (node.right === null) {
console.log(node.data)
break
}
node = node.right
}
}
// 查找最小值
getMin() {
let node = this.root
while (true) {
if (node.left === null) {
console.log(node.data)
break
}
node = node.left
}
}
getSomeNum(num) {
let node = this.root
while (node) {
if (num < node.data) {
node = node.left
} else if (num > node.data) {
node = node.right
} else if (num === node.data) {
console.log('node.data:', node.data)
break
}
}
}
getSmallest(ndoe) {
while (node.left !== null) {
node = node.left
}
return node
}
remove(data) {
this.root = this.removeNode(this.root, data)
}
// 若是要刪除的節點是當前節點,則返回null,不然返回原節點,並按照規則,遞歸查找下一個節點是否是目標節點
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 = this.getSmallest(node.right)
node.data = tempNode.data
node.right = this.removeNode(node.right, tempNode.data)
return node
} else if (data < node.data) {
node.left = this.removeNode(node.left, data)
return node
} else {
node.right = this.removeNode(node.right, data)
return node
}
}
}
let bst = new BST()
bst.insert(8)
bst.insert(3)
bst.insert(6)
bst.insert(4)
bst.insert(9)
bst.insert(11)
bst.insert(2)
bst.insert(5)
bst.insert(7)
bst.remove(9)
bst.inOrder(bst.root)
// 打印結果
2
3
4
5
6
7
8
11
複製代碼
從打印結果看,須要刪除的數據被刪除了,bst能正常遍歷,中序遍歷順序也正確,說明咱們刪除程序沒有問題。自此關於二叉樹的知識點就基本講完了。有實際狀況時根據實際狀況再調整。