BST 解析 (一)


這篇博文主要初步介紹Binary Search Tree(BST)的一些基本功能以及應用場景,由於BST的相關知識比較多,下一節會接着補充BST的一些功能。這一節主要分為以下六個要素:

  1. BST 的定義
  2. BST的應用場景
  3. BST searching 分析
  4. BST insertion 分析
  5. 最大值/最小值的查找
  6. Next Larger Key的分析

一:BST的定義

invariant:

BST是對於任意的node x,如果node y是node x的左邊的節點, 那么Key(y) <= Key(x); 對於任意的node x, 如果node y 是node x的右邊的節點, 那么key(y)>=key(x).

下圖是一個BST結構的example:

注意上面的樹狀結構和Heap很相似,但其實他們是有非常大的本質區別的;heap的結構本質是array,每一個node本身是沒有一個指向children和parent的pointer的。然而BST的每一個node都是包含有指向它的children和parent的pointer。 BST中的node的C++的實現如下:

class Node{
    
private:
    int key;
    Node *left;
    Node *right;
    Node *parent;
    
public:
    //constructor
    Node();
    //key set & get
    void setKey(int innerKey);
    int getKey();
    //left child pointer's get & set
    void setLeft(Node *innerLeft);
    Node * getLeft();
    //right child pointer's get & set
    void setRight(Node *innerRight);
    Node *getRight();
    //parent pointer's get & set
    void setParent(Node *innerParent);
    Node *getParent();
    
}

 

 

二:BST的應用場景

例如比賽場館的預訂這個案例中,要求預訂的時間前后一個小時這個場館並沒有被其他人預訂;在這個案例中,我的預訂時間前面之前的被別人預訂過的時間肯定小於我的預訂時間, 而后面的預訂時間肯定大於我的預訂時間, 如果想成功預訂還得繼續比較他們的中間時間差是否大於1小時。為了更好的描述這個案例, 我用下面的圖示來幫助說明:

 

 

上面這就是一個典型的BST的案例,雖然我是用一個一維的coordinate表示,但其本質就是一個BST。

 

三:BST searching 分析

如果有-個BST的數據結構,讓我們找一個key,那么這個過程是怎樣的呢?首先第一步從root開始比較,如果小於root的key則跟left child比較,否則跟right child接着比較;如此遞歸下去直到找到我們的key或者一直到了BST的leaf。如果找到key,則返回這個key;如果一直到了leaf都沒有找到,則說明這個key根本就不在這個BST,返回NULL。這個過程是一直比較的過程,所有步驟中的basic operation都是比較的話,稱之為Comparison Model. 下面兩符圖分別展示了返回key和返回NULL 的2中情況:

 

 

下面代碼的過程是用C++實現BST 的searching的過程。

/*
 *Description: search(int key) is publically invoked, in order to search the key in the BST
 *
 *parameters:
            1. key //the value which we want to find within the BST
 *
 *return void
 *
 */
Node *BST::search(int key){
    
    if (this->root == NULL) {//the BST has not initialzed yet
        
        return NULL;
        
    }else{
        
       return search(key, this->root);
    }
}


/*
 *Description: recuresivly to search the key, the work starts from root and level by level down to leaf, if fails to find out, return NULL.
 *
 *
 *parameters:
 *           1: key//value which should be searched
 *           2: node//this is the node the the key compares with
 *
 *return Node *
 **/
Node* BST::search(int key, Node *node){
    
    if (node==NULL) {//we have found all the nodes, but no one matches, which means it is not within the BST
     
        return NULL;
        
    }else if (key == node->getKey()) {//we spot on the key, return the node
            
            return node;
            
    }else if (key < node->getKey()){//the key is smaller than the node, so it is must be in the left subtree.
            
        return search(key, node->getLeft());
            
    }else{// the key is bigger than the node, so it is must be in the right subtree.
            
        return search(key, node->getRight());
            
    }

} 

根據以上的步驟的分析,BST search的worst case efficiency = height of BST, 即efficiency = O(h), h 是指BST的高度,注意這里的h不像Heap那樣是logN哦,這里的h是一個介於logN和N之間的一個值;當然了我們可以通過BST的balancing過程將BST的高度都轉換成logN,但這一塊比較復雜需要在后面講解。現在我們只需要知道是BST的height就行。 

四:BST 的insertion分析

如果希望向BST插入一個node (稱為A),並且繼續保持BST的結構,則需要通過以下幾個步驟:

  • 從root開始比較,如果A的key小於node,則跟node的left child接着比較,如果大於等於node的key,則跟node的right child的key接着比較;一直比較到leaf為止
  • 如果A的key值小於leaf的值,則將node A 插入到當前leaf的左邊,否則插入node的右邊;

其具體的圖示過程如下所示:

insertion的c++實現過程如下代碼所示:

/*
 *insert(int key) is publically invoked, and the key could be inserted at proper position
 *
 *parameters:
            1. key //the value of an node
 
 *
 *return void
 *
 */
void BST::insert(int key){
    
    if (this->root != NULL) {//The BST has already intialized, so we need to compare the keys level by level according to the BST critirals.
        
        //we gonna start from the root node to compare values until we find a leaf
        insert(key, this->root);
        
    }else{
        
        Node *rootNode = new Node();
        this->root = rootNode;
        this->root->setKey(key);
        this->root->setLeft(NULL);
        this->root->setRight(NULL);
        this->root->setParent(NULL);
        
    }
}

/*
 *Description: recuresivly to find where the key should be inserted, the work starts from root and level by level down to leaf
 *
 *CAUTION: WE DO NOT DISCUSS DUPLICATE KEYS HERE
 *
 *parameters:
 *           1: key//value which should be inserted
 *           2: node//this is the node the the key compares with
 *
 *return void
 **/

void BST::insert(int key, Node *node){
    
    if (key<node->getKey()) {//indicates that the key should be in the left side of node;
        
        if (node->getLeft()!=NULL) {//node's left child is not null, so we need to down to search
            
            insert(key, node->getLeft());
            
        }else{//node is a leaf, we have hit the spot
            
            Node *newNode = new Node();
            newNode->setKey(key);
            newNode->setLeft(NULL);
            newNode->setRight(NULL);
            newNode->setParent(node);
            node->setLeft(newNode);
           
            
        }
        
        
    }else if (key > node->getKey()){//indicates that the key should be in the right side of node;

        if (node->getRight()!=NULL) {//node's right child is not null, so we need to search down
            
            insert(key, node->getRight());
            
        }else{//node is a leaf, spot on
            
            Node *newNode = new Node();
            newNode->setKey(key);
            newNode->setLeft(NULL);
            newNode->setRight(NULL);
            newNode->setParent(node);
            node->setRight(newNode);
            
        }
        
    }
    
}

上面的過程和BST的search很類似,也是通過不斷的比較,只是在最后在leaf后面插入一個元素,找到leaf后插入的動作可以看做是常量O(1)。那么insertion 的時間復雜度是:efficiency = O(h)+O(1)=O(h); 所以它同樣是BST的高度height。

 

五:最值的查找

由於BST自身特點和結構,我們其實非常容易就可以找到最大值和最小值的。由於BST中比當前node小的node始終在左邊,而比當前node大的node始終在自己的右邊。所以我們就可以從root開始一直開始遍歷node的left child,知道leftchild等於nil為止,那么就說明這個node就是最小值;相反如果找最大值,就一直找node的right child,直到right child為nil為止。其具體的圖例和實現過程如:

 

/*
 *Description: The function is to find out the minimum key node within the subtree rooted at self
 *
 *
 *parameters: void
 *
 *
 *return: node;//the minimum node
 *
 *
 *****/
Node * Node::findMin(){
    
    Node *currentNode = this;
    
    while (currentNode->getLeft() != NULL) {
        
        currentNode = currentNode->getLeft();
    }
    
    return currentNode;
}

最值的尋找的時間復雜度同樣是這個BST的高度height,即O(h);

六:next larger/smaller 分析

next larger 是指比某個node A 大的值,但是比所有其他大於node A的nodes 都要小;比較繞口,其實本質就是在所有比node A 大的集合中,key值最接近node A 的哪一個node。同理可知next smaller的含義。那么在BST中如何尋找一個node的next larger或者next smaller呢?看下圖展示尋找next larger的過程

 

 

根據以上尋找next larger的圖示,可以總結出以下2條規律:

1. 如果node有right subtree,那么這個node的next larger就是它的right subtree的最小值

2.如果node沒有subtree, 那么這個node的next larger就需要一直的往parent node traversal, 直到node 是 它的left children tree的一個節點為止。

那么它的worst case的時間復雜度同樣是0(h)。

next larger的實現代碼如下:

/*
 *
 *Description: this function's aim is to find the next next of this node
 *
 *
 *parameters: void
 *
 *
 *return: Node// the next larger node
 *
 *
 */
Node* Node::findNextLarger(){
    
    if (this->getRight() == NULL) {//this node does not have any right subtree
        
        Node *tempNode = this;
        
        while (tempNode->getParent()->getLeft() != tempNode) {
            
            
            tempNode = tempNode->getParent();
            
            if (tempNode == NULL) {//no next larger value exsits
                
                return NULL;
            }
            
        }
        
        return tempNode->getParent();
        
    }else{//this node does have a right subtree
        
        return this->getRight()->findMin();//return the right subtree's minimun key
        
    }
    
}

那么這章的BST(一)的內容就結束了,那么這里還有幾個小問題哈,從頭到尾我們沒有分析BST的height具體是多少,也沒有介紹delete node的過程,下一節我會分析這2塊。

如果有什么問題,歡迎大家指教。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM