零、我想說
這是在對二叉搜尋樹的學習過程中的小記錄, 如果有錯誤的地方,歡迎大家和我一起討論學習,一起進步~
一、 二叉搜尋樹的結構
【程式碼實現】
class Node{
constructor(data, lChild, rChild) {
this.data = data //節點儲存的資料
this.lChild = lChild //節點的左孩子
this.rChild = rChild //節點的右孩子
}
}
class BinarySearchTree {
constructor () {
this.root = null //二叉搜尋樹的根節點
}
}
複製程式碼
二、二叉搜尋樹插入節點
【思路】
- 新建待插入節點
- 定義插入方法(根據二叉搜尋樹的特點:對於任意一個結點,其左子樹中最大關鍵字都不超過x.data,其右子樹中最小關鍵字都不小於x.data)
- 如果根節點為空的話,將插入節點賦值給根節點,否則遞迴呼叫步驟2中的插入方法
【程式碼實現】
insert(data) {
//1.新建節點
let newNode = new Node(data, null, null)
//2.定義插入的方法(根據二叉搜尋樹的特點):
//給方法插入兩個引數:開始查詢的節點,待插入節點
/*
2.1 如果待插入節點比當前節點小的話,
* 如果當前節點的左孩子為空,則直接賦值給當前節點的左孩子,
否則遞迴呼叫插入方法,也就是找到空的左節點
注意開始查詢的節點應該是當前節點的左孩子
2.1 如果待插入節點比當前節點大的話,
* 如果當前節點的右孩子為空,則直接賦值給當前節點的右孩子,
否則遞迴呼叫插入方法,也就是找到空的右節點
注意開始查詢的節點應該是當前節點的右孩子
*/
let insertNode = (currentNode, newNode) => {
if (newNode.data < currentNode.data) {
if (currentNode.lChild === null) {
currentNode.lChild = newNode
} else {
insertNode(currentNode.lChild, newNode)
}
} else {
if (currentNode.rChild === null) {
currentNode.rChild = newNode
} else {
insertNode(currentNode.rChild, newNode)
}
}
}
//3.如果根節點為空,直接賦值給空節點
if (this.root === null) {
this.root = newNode
} else {
//否則呼叫插入節點的方法
insertNode(this.root, newNode)
}
}
複製程式碼
【測試程式碼】
let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst)
複製程式碼
【測試結果】
三、二叉樹的遍歷
遍歷二叉樹,就是以某種方式逐個“訪問”二叉樹的每一個節點。
“訪問”是指對節點的進行某種操作,例如輸出節點的值。根據訪問樹根的順序,
我們有三種方式遍歷二叉樹:
-
前序遍歷:樹根->左子樹->右子樹
-
中序遍歷:左子樹->樹根->右子樹
-
後序遍歷:左子樹->右子樹->樹根
1.先序遍歷
【思路】
- 用陣列儲存結果
- 定義一個方法:如果是空節點則
return fase
否則按順序樹根->左子樹->右子樹
遞迴呼叫該方法 - 自動呼叫:從根節點開始訪問
- 返回結
【程式碼實現】
preOrder() {
let result = [] //用於儲存結果
let preOrderNode = (node) => {
if (node === null) return false
result.push(node.data) //訪問節點的data,將data放入result陣列中
preOrderNode(node.lChild)//節點的左孩子 遞迴呼叫該遍歷的方法
preOrderNode(node.rChild)//節點的右孩子 遞迴呼叫該遍歷的方法
}
preOrderNode(this.root) // 自動呼叫:從根節點開始訪問
return result
}
複製程式碼
【程式碼測試】
let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst.preOrder())
複製程式碼
【測試結果】
2.中序遍歷
【思路】
- 用陣列儲存結果
- 定義一個方法:如果是空節點則
return fase
否則按順序左子樹->樹根->右子樹
遞迴呼叫該方法 - 自動呼叫:從根節點開始訪問
- 返回結果
【程式碼實現】
midOrder() {
let result = [] //用於儲存結果
let midOrderNode = (node) => {
if (node === null) return false
midOrderNode(node.lChild)//節點的左孩子 遞迴呼叫該遍歷的方法
result.push(node.data) //訪問節點的data,將data放入result陣列中
midOrderNode(node.rChild)//節點的右孩子 遞迴呼叫該遍歷的方法
}
midOrderNode(this.root) // 自動呼叫:從根節點開始訪問
return result
}
複製程式碼
【程式碼測試】
let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst.midOrder())
複製程式碼
【測試結果】
3.後序遍歷
【思路】
- 用陣列儲存結果
- 定義一個方法:如果是空節點則
return fase
否則按順序左子樹->右子樹->樹根
遞迴呼叫該方法 - 自動呼叫:從根節點開始訪問
- 返回結果
【程式碼實現】
lastOrder() {
let result = [] //用於儲存結果
let lastOrderNode = (node) => {
if (node === null) return false
lastOrderNode(node.lChild)//節點的左孩子 遞迴呼叫該遍歷的方法
lastOrderNode(node.rChild)//節點的右孩子 遞迴呼叫該遍歷的方法
result.push(node.data) //訪問節點的data,將data放入result陣列中
}
lastOrderNode(this.root) // 自動呼叫:從根節點開始訪問
return result
}
複製程式碼
【程式碼測試】
let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst.lastOrder())
複製程式碼
【測試結果】
四、根據data查詢節點
【思路】
- 定義一個查詢的方法:
方法有兩個引數:一個是開始查詢的節點,另一個是查詢的data
如果查詢的值比當前值小的話,就遞迴呼叫該方法(注意用查詢節點的左孩子開始查詢),否則從查詢節點的右孩子開始查詢 - 自動呼叫方法
【程式碼實現】
searchNodeByData(data) {
//定義一個查詢的方法
//方法有兩個引數:一個是開始查詢的節點,另一個是查詢的data
const search = (fromNode, data) => {
console.log(fromNode)
if (fromNode === null) return false //遞迴結束條件
if (fromNode.data === data) return fromNode //如果找到,則返回節點
//如果查詢的值比當前值小的話,就遞迴呼叫該方法(注意用查詢節點的左孩子開始查詢),否則從查詢節點的右孩子開始查詢
return search((data < fromNode.data ? fromNode.lChild: fromNode.rChild), data)
}
return search(this.root, data) //自動呼叫方法
}
複製程式碼
【測試程式碼】
let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst.searchNodeByData(0))
console.log(bst.searchNodeByData(5))
console.log(bst.searchNodeByData(4))
複製程式碼
【測試結果】
五、二叉樹查詢最值
1.查詢最大值
【思路】
根據二叉搜尋樹的特點,最右邊的孩子就是最大的節點
【程式碼實現】
maxFrom(node) {
//定義查詢最大節點的的方法
const maxNode = node => {
if (node === null) return false //遞迴結束的條件
if (node.rChild == null) return node //該節點沒有右孩子了
return maxNode(node.rChild) //否則一直遞迴查詢右孩子
}
return maxNode(node || this.root) //如果沒有指定從哪個節點找起的話,預設從根節點開始查詢
}
複製程式碼
【測試程式碼】
let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst.maxFrom())
複製程式碼
【測試結果】
1.查詢最小值
【思路】
根據二叉搜尋樹的特點,最左邊的孩子就是最小的節點
【程式碼實現】
minFrom(node) {
//定義查詢最小節點的的方法
const minNode = node => {
if (node === null) return false //遞迴結束的條件
if (node.lChild === null) return node //該節點沒有左孩子了
return minNode(node.lChild) //否則一直遞迴查詢右孩子
}
return minNode(node || this.root) //如果沒有指定從哪個節點找起的話,預設從根節點開始查詢
}
複製程式碼
【測試程式碼】
let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst.minFrom())
複製程式碼
【測試結果】
六、我的程式碼
class Node{
constructor(data, lChild, rChild) {
this.data = data //節點儲存的資料
this.lChild = lChild //節點的左孩子
this.rChild = rChild //節點的右孩子
}
}
class BinarySearchTree {
constructor () {
this.root = null //二叉搜尋樹的根節點
}
insert(data) {
//1.新建節點
let newNode = new Node(data, null, null)
//2.定義插入的方法(根據二叉搜尋樹的特點):
//給方法插入兩個引數:開始查詢的節點,待插入節點
/*
2.1 如果待插入節點比當前節點小的話,
* 如果當前節點的左孩子為空,則直接賦值給當前節點的左孩子,
否則遞迴呼叫插入方法,也就是找到空的左節點
注意開始查詢的節點應該是當前節點的左孩子
2.1 如果待插入節點比當前節點大的話,
* 如果當前節點的右孩子為空,則直接賦值給當前節點的右孩子,
否則遞迴呼叫插入方法,也就是找到空的右節點
注意開始查詢的節點應該是當前節點的右孩子
*/
let insertNode = (currentNode, newNode) => {
if (newNode.data < currentNode.data) {
if (currentNode.lChild === null) {
currentNode.lChild = newNode
} else {
insertNode(currentNode.lChild, newNode)
}
} else {
if (currentNode.rChild === null) {
currentNode.rChild = newNode
} else {
insertNode(currentNode.rChild, newNode)
}
}
}
//3.如果根節點為空,直接賦值給空節點
if (this.root === null) {
this.root = newNode
} else {
//否則呼叫插入節點的方法
insertNode(this.root, newNode)
}
}
//前序遍歷
preOrder() {
let result = [] //用於儲存結果
let preOrderNode = (node) => {
if (node === null) return false
result.push(node.data) //訪問節點的data,將data放入result陣列中
preOrderNode(node.lChild)//節點的左孩子 遞迴呼叫該遍歷的方法
preOrderNode(node.rChild)//節點的右孩子 遞迴呼叫該遍歷的方法
}
preOrderNode(this.root) // 自動呼叫:從根節點開始訪問
return result
}
//中序遍歷
midOrder() {
let result = [] //用於儲存結果
let midOrderNode = (node) => {
if (node === null) return false
midOrderNode(node.lChild)//節點的左孩子 遞迴呼叫該遍歷的方法
result.push(node.data) //訪問節點的data,將data放入result陣列中
midOrderNode(node.rChild)//節點的右孩子 遞迴呼叫該遍歷的方法
}
midOrderNode(this.root) // 自動呼叫:從根節點開始訪問
return result
}
//後續遍歷
lastOrder() {
let result = [] //用於儲存結果
let lastOrderNode = (node) => {
if (node === null) return false
lastOrderNode(node.lChild)//節點的左孩子 遞迴呼叫該遍歷的方法
lastOrderNode(node.rChild)//節點的右孩子 遞迴呼叫該遍歷的方法
result.push(node.data) //訪問節點的data,將data放入result陣列中
}
lastOrderNode(this.root) // 自動呼叫:從根節點開始訪問
return result
}
//根據data查詢節點
searchNodeByData(data) {
//定義一個查詢的方法
//方法有兩個引數:一個是開始查詢的節點,另一個是查詢的data
const search = (fromNode, data) => {
if (fromNode === null) return false //遞迴結束條件
if (fromNode.data === data) return fromNode //如果找到,則返回節點
//如果查詢的值比當前值小的話,就遞迴呼叫該方法(注意用查詢節點的左孩子開始查詢),否則從查詢節點的右孩子開始查詢
return search((data < fromNode.data ? fromNode.lChild: fromNode.rChild), data)
}
return search(this.root, data) //自動呼叫方法
}
maxFrom(node) {
//定義查詢最大節點的的方法
const maxNode = node => {
if (node === null) return false //遞迴結束的條件
if (node.rChild == null) return node //該節點沒有右孩子了
return maxNode(node.rChild) //否則一直遞迴查詢右孩子
}
return maxNode(node || this.root) //如果沒有指定從哪個節點找起的話,預設從根節點開始查詢
}
minFrom(node) {
//定義查詢最小節點的的方法
const minNode = node => {
if (node === null) return false //遞迴結束的條件
if (node.lChild === null) return node //該節點沒有左孩子了
return minNode(node.lChild) //否則一直遞迴查詢右孩子
}
return minNode(node || this.root) //如果沒有指定從哪個節點找起的話,預設從根節點開始查詢
}
}
複製程式碼