數據結構的那些事(三)

繼續前面系列,這一章主要分析二叉樹和二叉查找樹。 樹的定義: node

咱們先來了解一下有關樹的術語:一棵樹最上面的節點稱爲根節點,若是下面鏈接多個節點,那麼該節點稱爲父節點,它下面的節點稱爲子節點。沒有任何子節點的節點稱爲葉子節點。

二叉樹是一種特殊的樹。它的子節點個數不超過兩個,一個父節點左邊的稱爲左節點,右邊的稱爲右節點。

正是由於二叉樹的這些特性,讓它的查找效率極高,從而引出了二叉查找樹,左子節點保存相對父節點較小的值,右節點保存相對父節點較大的值算法

接下來,咱們來一塊兒實現這個二叉查找樹。 咱們要定義一個對象Node來表示這棵樹:數組

function Node(data, left, right) {
    this.data = data;
    this.left = left;
    this.right = right;
    this.show = show;
}
function show() {
    return this.data;
}
複製代碼

這個Node對象能夠保存數據,也保存和其餘節點的連接。 如今,能夠建立一個類,用來表示二叉查找樹,簡稱BSTbash

function BST() {
    this.root = null;
    this.insert = insert;
    this.inOrder = inOrder;
}
function insert(data) {
    var n = new Node(data, null, null);
    if (this.root == null) {
        this.root = n;
    }else {
        var current = this.root;
        var parent;
        while (true) {
            parent = current;
            if (data < current.data) {
                current = current.left;
                if (current == null) {
                    parent.left = n;
                    break;
            }
        }else {
            current = current.right;
                if (current == null) {
                    parent.right = n;
                    break;
                }
            }
        }
    }
}
複製代碼

這個BST的insert方法有點複雜,咱們來分析下都作了那些事:數據結構

(1)首先建立一個沒有左右子節點的Node實例;post

(2)若是這是第一個插入BST的節點就做爲根節點;ui

(3)不是的話,獲取這個根節點賦值給current變量往下走this

(4)進入一個循環判斷,找到正確的插入點會跳出循環spa

(5)若是待插入節點保存的數據小於當前節點,則設新的當前爲原節點的左節點code

(6)若是當前節點的左節點爲null,就將新的節點插入這個位置,退出循環,反之,繼續往下循環查找。

(7)查找右節點也相似。

有了insert方法咱們就能插入節點建立一個二叉查找樹的數據結構。

有了二叉查找樹,接下來咱們該考慮怎麼遍歷這個BST

有三種遍歷BST的方法:分別是中序,先序和後序,其中中序遍歷最爲常見。

咱們先來看下中序遍歷是如何工做的:

有了這個思路咱們就能夠寫出這個中序遍歷的方法:

function inOrder(node) {
    if (!(node == null)) {
        inOrder(node.left);
        putstr(node.show() + " ");
        inOrder(node.right);
    }
}
複製代碼

這裏用到了遞歸的思想,該方法須要以升序訪問樹中全部節點,因此它首先會遞歸查找到最下面的左葉子節點,而後訪問左子樹,再訪問根節點,最後訪問右子樹。

先序遍歷:

上面是先序遍歷的訪問路徑, 根據上面的思路能夠寫出這樣的代碼:

preOrder(node) {
    if (!(node == null)) {
        putstr(node.show() + " ");
        preOrder(node.left);
        preOrder(node.right);
    }
}

複製代碼

注意:中序遍歷和先序遍歷的惟一區別,就是if語句中的代碼順序。

最後是後序遍歷

function postOrder(node){
    if(!(node == null)){
        postOrder(node.left);
        postOrder(node.right);
        putstr(node.show() + " ")
    }
}
複製代碼

查找最小值,最大值和給定值

由於較小的值老是在左子節點,在BST上查找最小值,只要遍歷左子樹,直到找到最後一個節點:

function getMin(){
    let current = this.root;
    while(!(current.left == null)){
        current = current.left;
    }
    return current.data
}
複製代碼

查找最大值只要遍歷右子樹直到找到最後一個節點便可。

查找給定值,稍微麻煩點,咱們須要將給定值和當前節點進行比較,來決定是左遍歷仍是右遍歷。

function find(data){
    let current = this.root;
    while(current !=null){
        if(current.data == data){
            return current
        }
        else if(data<current.data){
            current = current.left
        }else{
            current = current.right
        }
    }
    return null;
}
複製代碼

若是找到給定值,返回給定值,找不到會返回null

從二叉樹上刪除節點

從BST上刪除節點的操做最複雜,由於咱們要考慮幾種狀況:

(1)是否包含待刪除的數據

(2)待刪除節點是不是葉子節點

(3)待刪除節點是否只包含一個子節點

(4)待刪除節點是否包含兩個子節點

爲了方便,咱們將刪除節點拆分紅兩個方法,remove()方法接收待刪除的數據,removeNode()方法刪除節點:

function remove(data){
    root = removeNode(this.root, data)
}

function 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 = getSmallest(node.right);
        node.data = tempNode.data;
        node.right = removeNode(node.right, tempNode.data);
        return node
    }
    else if (data < node.data){
        node.left = removeNode(node.left, data);
    }
    else {
        node.right = removeNode(node.right, data);
    }
}
複製代碼

上面,咱們就完成了二叉查找樹的增刪改查。

咱們以前有說到:

數組 的搜索比較方便,能夠直接用下標,但刪除或者插入某些元素就比較麻煩。 鏈表 與之相反,刪除和插入元素很快,但查找很慢。 二叉查找樹 就既有鏈表的好處,也有數組的好處。 在處理大批量的動態的數據是比較有用。

可是,人無完人,當考慮到二叉查找樹的隨機性,在最壞的狀況下(就是那種一條腿的感受),時間複雜度和順序查找差很少,這就讓人有點沒法接受了。

究其緣由,仍是由於左右子樹相差懸殊致使的,因而有人經過算法研究出了平衡二叉查找樹 ,也就是紅黑樹

就長這樣,這個要講清楚又是一個大東西,博主也沒徹底研究明白,等後續有時間弄清楚了奉賢給你們0.0

相關文章
相關標籤/搜索