二叉排序樹的特徵
二叉排序樹或者是一棵空樹,或者是具有如下特性的二叉樹:
1.每一元素都有一個鍵值, 而且不允許重複;
2.若它的左子樹不空,則左子樹上所有結點的值均小於根結點的值;
3.若它的右子樹不空,則右子樹上所有結點的值均大於根結點的值;
4.它的左、右子樹也都分別是二叉排序樹。
二叉排序樹保存的元素構造
- template <typename Type>
- class Element
- {
- public:
- Element(const Type& _key): key(_key) {}
- Element():key(0) {}
- Type key;
- //在這兒可以很容易的添加更多的數據
- //方便對Element進行擴展
- };
二叉排序樹節點的設計與實現
- template <typename Type>
- class BstNode
- {
- friend class BsTree<Type>;
- public:
- BstNode(const Element<Type> &_data = 0,
- BstNode *_leftChild = NULL,
- BstNode *_rightChild = NULL)
- : data(_data), leftChild(_leftChild), rightChild(_rightChild) {}
- const Type &getData() const
- {
- return data.key;
- }
- private:
- //Node當中保存的是Element元素
- Element<Type> data;
- BstNode *leftChild;
- BstNode *rightChild;
- void display(int i);
- };
- //中序遍歷二叉樹:
- //能夠保證該二叉樹元素按照遞增順序打印出來
- template <typename Type>
- void BstNode<Type>::display(int i)
- {
- //首先訪問左子樹
- if (leftChild != NULL)
- leftChild->display(2*i);
- //訪問中間節點
- //Number表示爲如果該樹爲完全二叉樹/滿二叉樹, 其編號爲幾
- std::cout << "Number: " << i << ", data.key = " << data.key << std::endl;
- //訪問右子樹
- if (rightChild != NULL)
- rightChild->display(2*i+1);
- }
二叉排序樹的構造
- template <typename Type>
- class BsTree
- {
- public:
- //構造與析構
- BsTree(BstNode<Type> *init = NULL): root(init) {}
- ~BsTree()
- {
- if (!isEmpty())
- makeEmpty(root);
- }
- //二叉查找樹的三大主力:插入, 刪除, 搜索(又加入了一個迭代搜索)
- //插入
- bool insert(const Element<Type> &item);
- //刪除
- void remove(const Element<Type> &item)
- {
- remove(item, root);
- }
- //遞歸搜索
- const BstNode<Type>* search(const Element<Type> &item)
- {
- return search(item, root);
- }
- //迭代搜索
- const BstNode<Type> *searchByIter(const Element<Type> &item);
- //實用函數
- void display() const
- {
- if (root != NULL)
- root->display(1);
- }
- void visit(BstNode<Type> * currentNode) const
- {
- std::cout << "data.key = "
- << currentNode->data.key << std::endl;
- }
- bool isEmpty() const
- {
- return root == NULL;
- }
- void makeEmpty(BstNode<Type> *subTree);
- //中序遍歷
- void levelOrder() const;
- private:
- const BstNode<Type>* search(const Element<Type> &item,
- const BstNode<Type> *currentNode);
- void remove(const Element<Type> &item,
- BstNode<Type> *¤tNode);
- private:
- BstNode<Type> *root;
- };
二叉排序樹的插入算法
根據動態查找表的定義,“插入”操作在查找不成功時才進行;若二叉排序樹爲空樹,則新插入的結點爲新的根結點;否則,新插入的結點必爲一個新的葉子結點,其插入位置由查找過程得到。
- //二叉排序樹插入的實現與解析
- template <typename Type>
- bool BsTree<Type>::insert(const Element<Type> &item)
- {
- //如果這是新插入的第一個節點
- if (root == NULL)
- {
- root = new BstNode<Type>(item);
- root->leftChild = root->rightChild = NULL;
- return true;
- }
- BstNode<Type> *parentNode = NULL; //需要插入位置的父節點
- BstNode<Type> *currentNode = root; //需要插入的位置
- while (currentNode != NULL)
- {
- //如果二叉樹中已經含有了該元素, 則返回插入出錯
- if (item.key == currentNode->data.key)
- return false;
- parentNode = currentNode;
- //如果要插入的元素大於當前指向的元素
- if (item.key < currentNode->data.key)
- currentNode = currentNode->leftChild; //向左搜索
- else
- currentNode = currentNode->rightChild; //向右搜索
- }
- //此時已經查找到了一個比較合適的插入位置了
- if (item.key < parentNode->data.key)
- parentNode->leftChild = new BstNode<Type>(item);
- else
- parentNode->rightChild = new BstNode<Type>(item);
- return true;
- }
二叉排序樹的查找算法
若二叉排序樹爲空,則查找不成功;否則:
1.若給定值等於根結點的關鍵字,則查找成功;
2.若給定值小於根結點的關鍵字,則繼續在左子樹上進行查找;
3.若給定值大於根結點的關鍵字,則繼續在右子樹上進行查找。
- //二叉排序樹搜索的設計與實現
- //遞歸搜索
- template <typename Type>
- const BstNode<Type>* BsTree<Type>::search(const Element<Type> &item,
- const BstNode<Type> *currentNode)
- {
- if (currentNode == NULL)
- return NULL;
- if (currentNode->data.key == item.key)
- return currentNode;
- if (item.key < currentNode->data.key)
- return search(item, currentNode->leftChild);
- else
- return search(item, currentNode->rightChild);
- }
- //迭代搜索
- template <typename Type>
- const BstNode<Type> *BsTree<Type>::searchByIter(const Element<Type> &item)
- {
- for (BstNode<Type> *searchNode = root;
- searchNode != NULL;
- /*empty*/)
- {
- if (item.key == searchNode->data.key)
- return searchNode;
- if (item.key < searchNode->data.key)
- searchNode = searchNode->leftChild;
- else
- searchNode = searchNode->rightChild;
- }
- return NULL;
- }
二叉排序樹的刪除算法
和插入相反,刪除在查找成功之後進行,並且要求在刪除二叉排序樹上某個結點之後,仍然保持二叉排序樹的特性。
刪除分三種情況:
1.被刪除的結點是葉子節點:其雙親結點中相應指針域的值改爲“空”, 並將該節點刪除;
2.被刪除的結點只有左子樹或者只有右子樹:其雙親結點的相應指針域的值改爲 “指向被刪除結點的左子樹或右子樹”, 然後刪除該節點;
3.被刪除的結點既有左子樹,也有右子樹:以其前驅替代之,然後再刪除該前驅結點;
- //二叉排序樹節點刪除的實現與解析如下
- template <typename Type>
- void BsTree<Type>::remove(const Element<Type> &item,
- BstNode<Type> *¤tNode)
- {
- if (currentNode != NULL)
- {
- //如果要刪除的元素小於當前元素
- if (item.key < currentNode->data.key)
- remove(item, currentNode->leftChild); //向左搜索刪除
- //如果要刪除的元素大於當前元素
- else if (item.key > currentNode->data.key)
- remove(item, currentNode->rightChild); //向右搜索刪除
- //如果要刪除掉的元素等於當前元素(找到要刪除的元素了)
- // 並且當前節點的左右子女節點都不爲空
- else if ((currentNode->leftChild != NULL) && (currentNode->rightChild != NULL))
- {
- //從當前節點的右子女節點開始,
- //不斷向左尋找, 找到從當前節點開始中序遍歷的第一個節點
- //找到的這一個節點是在當前子樹中, 大於要刪除的節點的第一個節點
- BstNode<Type> *tmp = currentNode->rightChild;
- while (tmp->leftChild != NULL)
- tmp = tmp->leftChild;
- //用搜索到的節點值覆蓋要刪除的節點值
- currentNode->data.key = tmp->data.key;
- //刪除搜索到的節點
- remove(currentNode->data, currentNode->rightChild);
- }
- //如果當前節點就是要刪除的節點
- //並且其左子女(和/或)右子女爲空
- //默認包含了左右子女同時爲空的情況:
- //即: 在if中肯定爲true
- else
- {
- BstNode<Type> *tmp = currentNode;
- //如果左子女爲空
- if (currentNode->leftChild == NULL)
- //則用他的右子女節點頂替他的位置
- currentNode = currentNode->rightChild;
- //如果右子女爲空
- else
- //則用他的左子女節點頂替他的位置
- currentNode = currentNode->leftChild;
- //釋放節點
- delete tmp;
- }
- }
- }
二叉查找樹的幾個實用操作
- //清空二叉樹
- template <typename Type>
- void BsTree<Type>::makeEmpty(BstNode<Type> *subTree)
- {
- if (subTree != NULL)
- {
- if (subTree->leftChild != NULL)
- makeEmpty(subTree->leftChild);
- if (subTree->rightChild != NULL)
- makeEmpty(subTree->rightChild);
- delete subTree;
- }
- }
- //二叉查找樹的層次遍歷
- template <typename Type>
- void BsTree<Type>::levelOrder() const
- {
- std::queue< BstNode<Type> * > queue;
- queue.push(root);
- while (!queue.empty())
- {
- BstNode<Type> *currentNode = queue.front();
- queue.pop();
- visit(currentNode);
- if (currentNode->leftChild != NULL)
- queue.push(currentNode->leftChild);
- if (currentNode->rightChild != NULL)
- queue.push(currentNode->rightChild);
- }
- }
二叉排序樹的性能分析
對於每一棵特定的二叉排序樹,均可按照平均查找長度的定義來求它的 ASL 值,顯然,由值相同的 n 個關鍵字,構造所得的不同形態的各棵二叉排序樹的平均查找長度的值不同,甚至可能差別很大(如果二叉查找樹退化成一條鏈表, 則其插入/刪除/查找的性能都會退化爲O(N))。
但是在隨機情況下, 二叉排序樹的搜索, 插入, 刪除操作的平均時間代價爲O(logN);