接下來讓咱們一塊兒來探討js數據結構中的樹。這裏的樹類比現實生活中的樹,有樹幹,樹枝,在程序中樹是一種數據結構,對於存儲須要快速查找的數據非有用,它是一種分層數據的抽象模型。一個樹結構包含一系列存在父子關係的節點。每一個節點都有一個父節點以及零個或多個子節點。以下因此爲一個樹結構:)
前端
和樹相關的概念:1. 子樹:由節點和他的後代構成,如上圖標示處。2. 深度:節點的深度取決於它祖節點的數量,好比節點5有2個祖節點,他的深度爲2。3. 高度:樹的高度取決於全部節點深度的最大值。
二叉樹和二叉搜索樹介紹
1. 建立BinarySearchTree類
function BinarySearchTree(){
// 用於建立節點的類
let Node = function(key) {
this.key = key;
this.left = null;
this.right = null;
}
// 根節點
let root = null;
}
咱們將使用和鏈表相似的指針方式去表示節點之間的關係,若是不瞭解鏈表,請看我後序的文章《如何實現單向鏈表和雙向鏈表》。node
2.插入一個鍵
// 插入一個鍵
this.insert = function(key) {
let newNode = new Node(key);
root === null ? (root = newNode) : (insertNode(root, newNode))
}
向樹中插入一個新的節點主要有如下三部分:1.建立新節點的Node類實例 --> 2.判斷插入操做是否爲根節點,是根節點就將其指向根節點 --> 3.將節點加入非根節點的其餘位置。web
insertNode的具體實現以下:算法
function insertNode(node, newNode){
if(newNode.key < node.key) {
node.left === null ? (node.left = newNode) : (insertNode(node.left, newNode))
}else {
node.right === null ? (node.right = newNode) : (insertNode(node.right, newNode))
}
}
這裏咱們用到遞歸,接下來要實現的search,del等都會大量使用遞歸,因此說不了解的能夠先自行學習瞭解。咱們建立一個二叉樹實例,來插入一個鍵:微信
let tree = new BinarySearchTree();
tree.insert(20);
tree.insert(21);
tree.insert(520);
tree.insert(521);
樹的遍歷
-
中序遍歷:以從最小到最大的順序訪問全部節點 -
先序遍歷:以優先於後代節點的順序訪問每一個節點 -
後序遍歷:先訪問節點的後代節點再訪問節點自己
-
中序排序
this.inOrderTraverse = function(cb){
inOrderTraverseNode(root, cb);
}
// 輔助函數
function inOrderTraverseNode(node, cb){
if(node !== null){
inOrderTraverseNode(node.left, cb);
cb(node.key);
inOrderTraverseNode(node.right, cb);
}
}
-
先序排序
// 先序排序 --- 優先於後代節點的順序訪問每一個節點
this.preOrderTraverse = function(cb) {
preOrderTraverseNode(root, cb);
}
// 先序排序輔助方法
function preOrderTraverseNode(node, cb) {
if(node !== null) {
cb(node.key);
preOrderTraverseNode(node.left, cb);
preOrderTraverseNode(node.right, cb);
}
}
-
後序排序
// 後續遍歷 --- 先訪問後代節點,再訪問節點自己
this.postOrderTraverse = function(cb) {
postOrderTraverseNode(root, cb);
}
// 後續遍歷輔助方法
function postOrderTraverseNode(node, cb) {
if(node !== null){
postOrderTraverseNode(node.left, cb);
postOrderTraverseNode(node.right, cb);
cb(node.key);
}
}
搜索樹中的值
-
最小值
// 最小值
this.min = function(){
return minNode(root)
}
function minNode(node) {
if(node) {
while(node && node.left !== null){
node = node.left;
}
return node.key
}
return null
}
// 最大值
this.max = function() {
return maxNode(root)
}
function maxNode(node) {
if(node){
while(node && node.right !== null){
node = node.right;
}
return node.key
}
return null
}
// 搜索樹中某個值
this.search = function(key) {
return searchNode(root, key)
}
// 搜索輔助方法
function searchNode(node, key){
if(node === null) {
return false
}
if(key < node.key) {
return searchNode(node.left, key)
} else if(key > node.key) {
return searchNode(node.right, key)
}else {
return true
}
}
-
移除一個節點
this.remove = function(key){
root = removeNode(root, key);
}
// 發現最小節點
function findMinNode(node) {
if(node) {
while(node && node.left !== null){
node = node.left;
}
return node
}
return null
}
// 移除節點輔助方法
function removeNode(node, key) {
if(node === null) {
return null
}
if(key < node.key){
node.left = removeNode(node.left, key);
return node
} else if( key > node.key){
node.right = removeNode(node.right, key);
return node
} else {
// 一個頁節點
if(node.left === null && node.right === null) {
node = null;
return node
}
// 只有一個子節點的節點
if(node.left === null) {
node = node.right;
return node
}else if(node.right === null) {
node = node.left;
return node
}
// 有兩個子節點的節點
let aux = findMinNode(node.right);
node.key = aux.key;
node.right = removeNode(node.right, aux.key);
return node
}
}
刪除節點須要考慮的狀況比較多,這裏咱們會使用和min相似的實現去寫一個發現最小節點的函數,當要刪除的節點有兩個子節點時,咱們要將當前要刪除的節點替換爲子節點中最大的一個節點的值,而後將這個子節點刪除。數據結構
至此,一個二叉搜索樹已經實現,可是還存在一個問題,若是樹的一遍很是深,將會存在必定的性能問題,爲了解決這個問題,咱們能夠利用AVL樹,一種自平衡二叉樹,也就是說任何一個節點的左右兩側子樹的高度之差最多爲1。函數
若是想學習更多js算法和數據結構,能夠繼續觀看哦~
post
點個在看,你最好看性能
本文分享自微信公衆號 - 趣談前端(beautifulFront)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。學習