本文包括:二叉搜索樹(建立、遍歷、搜索、插入等)、JavaScript 實現翻轉二叉樹node
二叉樹的定義:二叉樹的每一個結點至多隻有二棵子樹(不存在度大於2的結點),二叉樹的子樹有左右之分,次序不能顛倒。github
二叉查找樹(BST):又稱爲是二叉排序樹(Binary Sort Tree)或二叉搜索樹。二叉查找樹是二叉樹的一種,可是它只容許你在左側節點存儲(比父節點)小的值,在右側節點存儲(比父節點)大(或者等於)的值。算法
首先建立一個 BinarySearchTree
類。bash
// 使用了 ES6 的 Class 語法
class BinarySearchTree {
constructor() {
this.root = null
}
Node(key) {
let left = null
let right = null
return {
key,
left,
right
}
}
}
複製代碼
來看一下二叉查找樹的數據結構組織方式(沒有找到二叉搜索樹的先用二叉樹的代替一下):數據結構
二叉樹是經過指針(指向下一個節點)來表示節點之間的關係的,因此須要在聲明 Node 的時候,定義兩個指針,一個指向左邊,一個指向右邊。 還須要聲明一個 root 來保存樹的根元素。函數
class BinarySearchTree {
// ...省略前面的
insert (key) {
let newNode = this.Node(key)
if (this.root === null) {
// 若是根節點爲空,那麼插入的節點就爲根節點
this.root = newNode
} else {
// 若是根節點不爲空
this.insertNode(this.root, newNode)
}
}
insertNode (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)
}
}
}
}
複製代碼
由於使用了 class 因此沒有學過 class 的同窗能夠先看一下 ES6 的 class,再來看文章。post
仔細分析上面的代碼,多看幾遍就能夠了解其中的奧妙(也能夠本身在遊覽器中運行一下,插入幾個值試一下)。學習
運行一遍試一下:ui
let m = new BinarySearchTree()
m.insert(5)
m.insert(4)
m.insert(3)
m.insert(6)
m.insert(7)
複製代碼
會獲得這樣的結構:
{
key: 5,
left: {
key: 4,
left: {
key: 3,
left: null,
right: null
},
right: null
},
right: {
key: 6,
left: null,
right: {
key: 7,
left: null,
right: null
}
}
}
複製代碼
emmm,真複雜(本身看的都頭暈),仍是畫個圖吧。
會生成這樣一個二叉查找樹~,插入功能就算完成啦!
遍歷一棵樹是指訪問樹的每一個節點並對它們進行某種操做的過程。訪問樹會有三種方法:中序、先序、後續。下面分別講解
中序遍歷是一種以上行順序訪問 BST 全部節點的遍歷方式,也就是從最小到最大的順序進行訪問全部節點。具體方法,看代碼吧,配上圖多看兩遍代碼就能明白了(我是這麼認爲的)。
class BinarySearchTree {
// ...省略前面的
inOrderTraverse (callback) {
this.inOrderTraverseNode(this.root, callback)
}
inOrderTraverseNode (node, callback) {
if (node !== null) {
this.inOrderTraverseNode(node.left, callback)
callback(node.key)
this.inOrderTraverseNode(node.right, callback)
}
}
}
複製代碼
一樣,用圖展現一下遍歷的過程,具體過程看代碼多思考一下。
先序遍歷會先訪問節點自己,而後再訪問它的左側子節點,最後再訪問右側的節點。
class BinarySearchTree {
// ...省略前面的
preOrderTraverse (callback) {
this.preOrderTraverseNode(this.root, callback)
}
preOrderTraverseNode (node, callback) {
if (node !== null) {
callback(node.key)
this.preOrderTraverseNode(node.left, callback)
this.preOrderTraverseNode(node.right, callback)
}
}
}
複製代碼
仔細看代碼,發現和中序遍歷的區別不過是先執行了 callback
而後再遍歷左右。
後序遍歷則是先訪問節點的後代節點,而後再訪問節點自己。實現:
class BinarySearchTree {
// ...省略前面的
postOrderTraverse (callback) {
this.postOrderTraverseNode(this.root, callback)
}
postOrderTraverseNode (node, callback) {
if (node !== null) {
this.postOrderTraverseNode(node.left, callback)
this.postOrderTraverseNode(node.right, callback)
callback(node.key)
}
}
}
複製代碼
再仔細看代碼,發現和中序遍歷的區別不過是先執行了遍歷了左右,最後執行了 callback
。
慣例,畫張圖~
三種遍歷方式講完啦,不懂的能夠多看幾遍代碼哦~
在樹中,一般有三種常常使用的搜索類型:
下面一一列舉
首先咱們知道二叉搜索樹中的最小值在最左邊,最大值在最右邊。既然知道這個,那麼實現搜索最大和最小就十分簡單了。因此直接上代碼:
class BinarySearchTree {
// ...省略前面的
// 搜索最小
min () {
return this.minNode(this.root)
}
minNode (node) {
if (node) {
// 若是節點存在,並且左邊不爲 null
while (node && node.left !== null) {
node = node.left
}
return node.key
}
// 若是樹爲空,則返回 null
return null
}
// 搜索最大
max () {
return this.maxNode(this.root)
}
maxNode (node) {
if (node) {
while (node && node.right !== null) {
node = node.right
}
return node.key
}
return null
}
}
複製代碼
基本上的思路和遍歷節點差很少,具體看代碼。
class BinarySearchTree {
// ...省略前面的
search (key) {
return this.searchNode(this.root, key)
}
searchNode (node, key) {
if (node === null) {
return false
}
// 若是 key 比節點的值小,那麼搜索左邊的子節點,下面的相反
if (key < node.key) {
return this.searchNode(node.left, key)
} else if (key > node.key) {
return this.searchNode(node.right, key)
} else {
return true
}
}
}
複製代碼
翻轉一個二叉樹,直觀上看,就是把二叉樹的每一層左右順序倒過來。
例如:
Input:
4
/ \
2 7
/ \ / \
1 3 6 9
複製代碼
Output:
4
/ \
7 2
/ \ / \
9 6 3 1
複製代碼
仔細看就是先把最底下的節點反轉,而後上一個節點再翻轉。例如:1 - 3 反轉成 3 - 1,6 - 9 反轉成 9 - 6, 而後再讓 2 - 7 反轉。固然反過來也同樣,先反轉 2 - 7 也是能夠的。
因此具體的過程是:
最後看一下實現的代碼:
class BinarySearchTree {
// ...省略前面的
invertTree (node = this.root) {
if (node === null) {
return
}
this.invertTree(node.left)
this.invertTree(node.right)
this.exchange(node)
}
exchange (node) {
let temp = node.left
node.left = node.right
node.right = temp
}
}
複製代碼
這樣就簡單實現啦,舒服舒服~
所有代碼在這裏~
class BinarySearchTree {
constructor() {
this.root = null
}
Node(key) {
let left = null
let right = null
return {
key,
left,
right
}
}
insert(key) {
let newNode = this.Node(key)
if (this.root === null) {
// 若是根節點爲空,那麼插入的節點就爲根節點
this.root = newNode
} else {
this.insertNode(this.root, newNode)
}
}
insertNode(node, newNode) {
console.log(node)
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)
}
}
}
inOrderTraverse(callback) {
this.inOrderTraverseNode(this.root, callback)
}
inOrderTraverseNode(node, callback) {
if (node !== null) {
this.inOrderTraverseNode(node.left, callback)
callback(node.key)
this.inOrderTraverseNode(node.right, callback)
}
}
preOrderTraverse(callback) {
this.preOrderTraverseNode(this.root, callback)
}
preOrderTraverseNode(node, callback) {
if (node !== null) {
callback(node.key)
this.preOrderTraverseNode(node.left, callback)
this.preOrderTraverseNode(node.right, callback)
}
}
postOrderTraverse(callback) {
this.postOrderTraverseNode(this.root, callback)
}
postOrderTraverseNode(node, callback) {
if (node !== null) {
this.postOrderTraverseNode(node.left, callback)
this.postOrderTraverseNode(node.right, callback)
callback(node.key)
}
}
// 搜索最小
min() {
return this.minNode(this.root)
}
minNode(node) {
if (node) {
// 若是節點存在,並且左邊不爲 null
while (node && node.left !== null) {
node = node.left
}
return node.key
}
// 若是樹爲空,則返回 null
return null
}
// 搜索最大
max() {
return this.maxNode(this.root)
}
maxNode(node) {
if (node) {
while (node && node.right !== null) {
node = node.right
}
return node.key
}
return null
}
search(key) {
return this.searchNode(this.root, key)
}
searchNode(node, key) {
console.log('node-', node, '---', node === null, '-key-', key)
if (node === null) {
return false
}
// 若是 key 比節點的值小,那麼搜索左邊的子節點,下面的相反
if (key < node.key) {
return this.searchNode(node.left, key)
} else if (key > node.key) {
return this.searchNode(node.right, key)
} else {
console.log('didi')
return true
}
}
invertTree (node = this.root) {
if (node === null) {
return
}
this.invertTree(node.left)
this.invertTree(node.right)
this.exchange(node)
}
exchange (node) {
let temp = node.left
node.left = node.right
node.right = temp
}
}
複製代碼
文章是本身的學習的一個記錄,若是可以順便幫助你們學習一下,那就再好不過了。
可是由於本人技術技術有限,因此文章不免會有疏漏,歡迎指出。