數據結構基礎(17) --二叉查找樹的設計與實現

二叉排序樹的特徵

二叉排序樹或者是一棵空樹,或者是具有如下特性的二叉樹:

    1.每一元素都有一個鍵值, 而且不允許重複;

    2.若它的左子樹不空,則左子樹上所有結點的值均小於根結點的值;

    3.若它的右子樹不空,則右子樹上所有結點的值均大於根結點的值;

    4.它的左、右子樹也都分別是二叉排序樹。



二叉排序樹保存的元素構造

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template <typename Type>  
  2. class Element  
  3. {  
  4. public:  
  5.     Element(const Type& _key): key(_key) {}  
  6.     Element():key(0) {}  
  7.     Type key;  
  8.     //在這兒可以很容易的添加更多的數據  
  9.     //方便對Element進行擴展  
  10. };  

二叉排序樹節點的設計與實現

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template <typename Type>  
  2. class BstNode  
  3. {  
  4.     friend class BsTree<Type>;  
  5.   
  6. public:  
  7.     BstNode(const Element<Type> &_data = 0,  
  8.             BstNode *_leftChild = NULL,  
  9.             BstNode *_rightChild = NULL)  
  10.         : data(_data), leftChild(_leftChild), rightChild(_rightChild) {}  
  11.   
  12.     const Type &getData() const  
  13.     {  
  14.         return data.key;  
  15.     }  
  16.   
  17. private:  
  18.     //Node當中保存的是Element元素  
  19.     Element<Type> data;  
  20.     BstNode *leftChild;  
  21.     BstNode *rightChild;  
  22.   
  23.     void display(int i);  
  24. };  
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //中序遍歷二叉樹:  
  2. //能夠保證該二叉樹元素按照遞增順序打印出來  
  3. template <typename Type>  
  4. void BstNode<Type>::display(int i)  
  5. {  
  6.     //首先訪問左子樹  
  7.     if (leftChild != NULL)  
  8.         leftChild->display(2*i);  
  9.   
  10.     //訪問中間節點  
  11.     //Number表示爲如果該樹爲完全二叉樹/滿二叉樹, 其編號爲幾  
  12.     std::cout << "Number: " << i << ", data.key = " << data.key << std::endl;  
  13.   
  14.     //訪問右子樹  
  15.     if (rightChild != NULL)  
  16.         rightChild->display(2*i+1);  
  17. }  

二叉排序樹的構造

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template <typename Type>  
  2. class BsTree  
  3. {  
  4. public:  
  5. //構造與析構  
  6.     BsTree(BstNode<Type> *init = NULL): root(init) {}  
  7.     ~BsTree()  
  8.     {  
  9.         if (!isEmpty())  
  10.             makeEmpty(root);  
  11.     }  
  12.   
  13. //二叉查找樹的三大主力:插入, 刪除, 搜索(又加入了一個迭代搜索)  
  14.     //插入  
  15.     bool insert(const Element<Type> &item);  
  16.     //刪除  
  17.     void remove(const Element<Type> &item)  
  18.     {  
  19.         remove(item, root);  
  20.     }  
  21.     //遞歸搜索  
  22.     const BstNode<Type>* search(const Element<Type> &item)  
  23.     {  
  24.         return search(item, root);  
  25.     }  
  26.     //迭代搜索  
  27.     const BstNode<Type> *searchByIter(const Element<Type> &item);  
  28.   
  29. //實用函數  
  30.     void display() const  
  31.     {  
  32.         if (root != NULL)  
  33.             root->display(1);  
  34.     }  
  35.     void visit(BstNode<Type> * currentNode) const  
  36.     {  
  37.         std::cout << "data.key = "  
  38.                   << currentNode->data.key << std::endl;  
  39.     }  
  40.     bool isEmpty() const  
  41.     {  
  42.         return root == NULL;  
  43.     }  
  44.     void makeEmpty(BstNode<Type> *subTree);  
  45.     //中序遍歷  
  46.     void levelOrder() const;  
  47.   
  48. private:  
  49.     const BstNode<Type>* search(const Element<Type> &item,  
  50.                                 const BstNode<Type> *currentNode);  
  51.     void remove(const Element<Type> &item,  
  52.                 BstNode<Type> *¤tNode);  
  53.   
  54. private:  
  55.     BstNode<Type> *root;  
  56. };  

二叉排序樹的插入算法

    根據動態查找表的定義,插入操作在查找不成功時才進行;若二叉排序樹爲空樹,則新插入的結點爲新的根結點;否則,新插入的結點必爲一個新的葉子結點,其插入位置由查找過程得到。

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //二叉排序樹插入的實現與解析  
  2. template <typename Type>  
  3. bool BsTree<Type>::insert(const Element<Type> &item)  
  4. {  
  5.     //如果這是新插入的第一個節點  
  6.     if (root == NULL)  
  7.     {  
  8.         root = new BstNode<Type>(item);  
  9.         root->leftChild = root->rightChild = NULL;  
  10.         return true;  
  11.     }  
  12.   
  13.     BstNode<Type> *parentNode = NULL;   //需要插入位置的父節點  
  14.     BstNode<Type> *currentNode = root;  //需要插入的位置  
  15.     while (currentNode != NULL)  
  16.     {  
  17.         //如果二叉樹中已經含有了該元素, 則返回插入出錯  
  18.         if (item.key == currentNode->data.key)  
  19.             return false;  
  20.   
  21.         parentNode = currentNode;  
  22.         //如果要插入的元素大於當前指向的元素  
  23.         if (item.key < currentNode->data.key)  
  24.             currentNode = currentNode->leftChild;   //向左搜索  
  25.         else  
  26.             currentNode = currentNode->rightChild;  //向右搜索  
  27.     }  
  28.   
  29.     //此時已經查找到了一個比較合適的插入位置了  
  30.     if (item.key < parentNode->data.key)  
  31.         parentNode->leftChild = new BstNode<Type>(item);  
  32.     else  
  33.         parentNode->rightChild = new BstNode<Type>(item);  
  34.   
  35.     return true;  
  36. }  

二叉排序樹的查找算法

若二叉排序樹爲空,則查找不成功;否則:

    1.若給定值等於根結點的關鍵字,則查找成功;

    2.若給定值小於根結點的關鍵字,則繼續在左子樹上進行查找;

    3.若給定值大於根結點的關鍵字,則繼續在右子樹上進行查找。

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //二叉排序樹搜索的設計與實現  
  2. //遞歸搜索  
  3. template <typename Type>  
  4. const BstNode<Type>* BsTree<Type>::search(const Element<Type> &item,  
  5.         const BstNode<Type> *currentNode)  
  6. {  
  7.     if (currentNode == NULL)  
  8.         return NULL;  
  9.     if (currentNode->data.key == item.key)  
  10.         return currentNode;  
  11.   
  12.     if (item.key < currentNode->data.key)  
  13.         return search(item, currentNode->leftChild);  
  14.     else  
  15.         return search(item, currentNode->rightChild);  
  16. }  
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //迭代搜索  
  2. template <typename Type>  
  3. const BstNode<Type> *BsTree<Type>::searchByIter(const Element<Type> &item)  
  4. {  
  5.     for (BstNode<Type> *searchNode = root;  
  6.             searchNode != NULL;  
  7.             /*empty*/)  
  8.     {  
  9.         if (item.key == searchNode->data.key)  
  10.             return searchNode;  
  11.   
  12.         if (item.key < searchNode->data.key)  
  13.             searchNode = searchNode->leftChild;  
  14.         else  
  15.             searchNode = searchNode->rightChild;  
  16.     }  
  17.   
  18.     return NULL;  
  19. }  

二叉排序樹的刪除算法

    和插入相反,刪除在查找成功之後進行,並且要求在刪除二叉排序樹上某個結點之後,仍然保持二叉排序樹的特性

 

刪除分三種情況:

    1.被刪除的結點是葉子節點:其雙親結點中相應指針域的值改爲“空”, 並將該節點刪除;

    2.被刪除的結點只有左子樹或者只有右子樹:其雙親結點的相應指針域的值改爲 “指向被刪除結點的左子樹或右子樹”, 然後刪除該節點;

    3.被刪除的結點既有左子樹,也有右子樹:以其前驅替代之,然後再刪除該前驅結點;

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //二叉排序樹節點刪除的實現與解析如下  
  2. template <typename Type>  
  3. void BsTree<Type>::remove(const Element<Type> &item,  
  4.                           BstNode<Type> *¤tNode)  
  5. {  
  6.     if (currentNode != NULL)  
  7.     {  
  8.         //如果要刪除的元素小於當前元素  
  9.         if (item.key < currentNode->data.key)  
  10.             remove(item, currentNode->leftChild);   //向左搜索刪除  
  11.         //如果要刪除的元素大於當前元素  
  12.         else if (item.key > currentNode->data.key)  
  13.             remove(item, currentNode->rightChild);  //向右搜索刪除  
  14.         //如果要刪除掉的元素等於當前元素(找到要刪除的元素了)  
  15.         // 並且當前節點的左右子女節點都不爲空  
  16.         else if ((currentNode->leftChild != NULL) && (currentNode->rightChild != NULL))  
  17.         {  
  18.             //從當前節點的右子女節點開始,  
  19.             //不斷向左尋找, 找到從當前節點開始中序遍歷的第一個節點  
  20.             //找到的這一個節點是在當前子樹中, 大於要刪除的節點的第一個節點  
  21.             BstNode<Type> *tmp = currentNode->rightChild;  
  22.             while (tmp->leftChild != NULL)  
  23.                 tmp = tmp->leftChild;  
  24.   
  25.             //用搜索到的節點值覆蓋要刪除的節點值  
  26.             currentNode->data.key = tmp->data.key;  
  27.             //刪除搜索到的節點  
  28.             remove(currentNode->data, currentNode->rightChild);  
  29.         }  
  30.         //如果當前節點就是要刪除的節點  
  31.         //並且其左子女(和/或)右子女爲空  
  32.         //默認包含了左右子女同時爲空的情況:  
  33.         //即: 在if中肯定爲true  
  34.         else  
  35.         {  
  36.             BstNode<Type> *tmp = currentNode;  
  37.             //如果左子女爲空  
  38.             if (currentNode->leftChild == NULL)  
  39.                 //則用他的右子女節點頂替他的位置  
  40.                 currentNode = currentNode->rightChild;  
  41.             //如果右子女爲空  
  42.             else  
  43.                 //則用他的左子女節點頂替他的位置  
  44.                 currentNode = currentNode->leftChild;  
  45.             //釋放節點  
  46.             delete tmp;  
  47.         }  
  48.     }  
  49. }  

二叉查找樹的幾個實用操作

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //清空二叉樹  
  2. template <typename Type>  
  3. void BsTree<Type>::makeEmpty(BstNode<Type> *subTree)  
  4. {  
  5.     if (subTree != NULL)  
  6.     {  
  7.         if (subTree->leftChild != NULL)  
  8.             makeEmpty(subTree->leftChild);  
  9.         if (subTree->rightChild != NULL)  
  10.             makeEmpty(subTree->rightChild);  
  11.   
  12.         delete subTree;  
  13.     }  
  14. }  
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //二叉查找樹的層次遍歷  
  2. template <typename Type>  
  3. void BsTree<Type>::levelOrder() const  
  4. {  
  5.     std::queue< BstNode<Type> * > queue;  
  6.     queue.push(root);  
  7.   
  8.     while (!queue.empty())  
  9.     {  
  10.         BstNode<Type> *currentNode = queue.front();  
  11.         queue.pop();  
  12.   
  13.         visit(currentNode);  
  14.         if (currentNode->leftChild != NULL)  
  15.             queue.push(currentNode->leftChild);  
  16.         if (currentNode->rightChild != NULL)  
  17.             queue.push(currentNode->rightChild);  
  18.     }  
  19. }  

二叉排序樹的性能分析

     對於每一棵特定的二叉排序樹,均可按照平均查找長度的定義來求它的 ASL 值,顯然,由值相同的 n 個關鍵字,構造所得的不同形態的各棵二叉排序樹的平均查找長度的值不同,甚至可能差別很大(如果二叉查找樹退化成一條鏈表, 則其插入/刪除/查找的性能都會退化爲O(N))。

     但是在隨機情況下, 二叉排序樹的搜索, 插入, 刪除操作的平均時間代價爲O(logN);


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章