二叉搜索樹:一個近似二分搜索的數據結構跳錶有點像,平衡二叉樹可以靠旋轉操作維護自平衡性,解決退化爲鏈表問題;跳錶通過隨機函數進行上層節點增刪,統計意義上維護平衡性。
概念:
二叉搜索樹:二叉搜索樹是由二叉樹來組織的,樹中每一個子樹的根節點key值都大於左樹的key值而小於右子樹的key值。
二叉搜索樹的中序遍歷key值恰好是從小到達排序。查詢二叉樹、MinMum、MaxMum、Insert和delete等操作時間複雜 度爲O(h); h爲樹的高度log2(n);
c++實現代碼:
頭文件,樹節點和樹 類的定義
Binary_SearchTree.h
#ifndef _BINARY_SEARCHTREE
#define _BINARY_SEARCHTREE
template<typename T>//定義Element類,便於外部調用函數更改數據類型
class Element
{
public:
T key;//便於在節點中隨時加入數據
char name;
};
template<typename T> class BST;//BST模板類的聲明
template<typename T>
class BSTNode
{
friend class BST<T>;//友元類聲明 BST二叉樹要訪問節點私有元素
public:
Element<T> getElement();
private:
Element<T> data;
BSTNode *leftChild;
BSTNode *rightChild;
void display(int i);//遞歸展示當前節點信息
};
template<typename T>//搜索二叉樹模板類
class BST
{
public:
BST(BSTNode<T>*init = 0);
bool BST_insert(const Element<T> &node);//插入元素
BSTNode<T>* BST_Search(const Element<T>&node);//遞歸搜索
BSTNode<T>* BST_Search(BSTNode<T>*,const Element<T>&);
BSTNode<T>*BST_Search_Iterator(const Element<T>&);//迭代搜索
bool Delete_Node(const Element<T>&node);//刪除節點
BSTNode<T>*MinMum();//樹的key最大節點
BSTNode<T>*MaxMum();//樹的key最小節點
void midOrder();//中序遍歷
void midOrder(BSTNode<T>*root);
void display();//遞歸左右子樹展示當前樹所有節點信息
~BST();
private:
BSTNode<T> *root;
};
#endif
hpp文件 實現類的成員函數
#include"Binary_SearchTree.h"
#include"iostream"
using namespace std;
//節點的成員函數
//節點內容獲取
template<typename T>
void BSTNode<T>::display(int i)
{
cout << "position: " << i << " data.Key:" << data.key << " value:" << data.name << endl;
if (leftChild)
leftChild->display(2 * i);
if (rightChild)
rightChild->display(2 * i + 1);
}
//獲取節點Element
template<typename T>
Element<T> BSTNode<T>::getElement()
{
return data;
}
//樹的成員函數
//構造初始化
template<typename T>
BST<T>::BST(BSTNode<T>*init = NULL)
{
root = init;
}
//插入節點
template<typename T>
bool BST<T>::BST_insert(const Element<T> &node)
{
BSTNode<T>*p = NULL;
BSTNode<T>*q = root;
while (q)//查找插入位置 最終肯定爲葉子
{
p = q;
if (node.key == q->data.key)
{
return false;
}
if (node.key > q->data.key)
{
q = q->rightChild;
}
else
{
q = q->leftChild;
}
}
//創建新節點保存插入元素
q = new BSTNode<T>;
q->data = node;//Element數據類型淺拷貝,若Element包含指針元素得重載=操作符
q->leftChild = NULL;
q->rightChild = NULL;
//空樹的情況
if (root == NULL)
{
root = q;
}
else
{
if (p->data.key > q->data.key)
{
p->leftChild = q;
}
else
{
p->rightChild = q;
}
}
return true;
}
//點展示樹左右子樹
//根據節點遞歸顯示左右子樹
template<typename T>
void BST<T>::display()
{
if (root == NULL)
{
cout << "樹爲空" << endl;
}
else
{
root->display(1);
}
}
//查找節點 遞歸方法
//遞歸的部分需要傳入根節點(BSTNode)
//外部以Element類型加載數據,故寫兩個函數,改爲一個Element端口
template<typename T>
BSTNode<T>*BST<T>::BST_Search(const Element<T>&node)
{
return BST_Search(root, node);
}
template<typename T>
BSTNode<T>*BST<T>::BST_Search(BSTNode<T>*b, const Element<T>&node)
{
if (b == NULL)
{
return NULL;
}
if (node.key == b->data.key)
{
return b;
}
else if (node.key < b->data.key)
{
return BST_Search(b->leftChild, node);
}
else
{
return BST_Search(b->rightChild, node);
}
}
//非遞歸查找 迭代方式實際中對大多數計算機更有效
template<typename T>
BSTNode<T>*BST<T>::BST_Search_Iterator(const Element<T>&node)
{
BSTNode<T>*t = root;
while (t)
{
if (node.key == t->data.key)
{
return t;
}
else if (node.key < t->data.key)
{
t = t->leftChild;
}
else
{
t = t->rightChild;
}
}
}
//刪除節點
template<typename T>
bool BST<T>::Delete_Node(const Element<T>&node)
{
if (root == NULL)
{
cout << "root == NULL" << endl;
return 0;
}
BSTNode<T> *del = NULL;
BSTNode<T> *pcurr = root;
BSTNode<T> *parent = NULL;
while (pcurr != NULL && pcurr->data.key != node.key)//先找到
{
if (node.key < pcurr->data.key)
{
parent = pcurr;
pcurr = pcurr->leftChild;
}
if (node.key > pcurr->data.key)
{
parent = pcurr;
pcurr = pcurr->rightChild;
}
}
if (pcurr == NULL)//找了一遍沒找到
{
return false;
}
if (pcurr->leftChild == NULL)//只有右孩子
{
if (pcurr == root)//如果是根節點 則根節點移動到右孩子即可
root = root->rightChild;
else if (pcurr == parent->leftChild)//若待刪節點是parent左孩子
{
parent->leftChild = pcurr->rightChild;
}
else if (pcurr == parent->rightChild)//若待刪除節點是parent右孩子
{
parent->rightChild = pcurr->rightChild;
}
del = pcurr;
}
else if (pcurr->rightChild == NULL)//只有左孩子
{
if (pcurr == root)
root = root->leftChild;
else if (pcurr == parent->leftChild)
{
parent->leftChild = pcurr->leftChild;
}
else
{
parent->rightChild = pcurr->leftChild;
}
del = pcurr;
}
else//既有左孩子又有右孩子 找到右子樹最小(最左)的元素替換,在按照以上方式刪除
{
//找到最左邊的節點
BSTNode<T> *left = pcurr->rightChild;
parent = pcurr;
while (left->leftChild)
{
parent = left;
left = left->leftChild;
}
del = left;
pcurr->data.key = left->data.key;//交換節點的值
if (parent->leftChild == left)
{
parent->leftChild = left->rightChild;
}
else//當pcurr只有一個右節點時,left是parent的右子樹
{
parent->rightChild = left->rightChild;
}
}
delete del;
}
//中序遍歷
template<typename T>
void BST<T>::midOrder()
{
midOrder(root);
}
template<typename T>
void BST<T>::midOrder(BSTNode<T>*root)
{
if (root == NULL)
{
return;
}
midOrder(root->leftChild);
cout<<root->getElement().key<<" "<<root->getElement().name<<"\t";
midOrder(root->rightChild);
}
//返回樹中最小的節點
template<typename T>
BSTNode<T>* BST<T>::MinMum()
{
BSTNode<T>*tmp = root;
while (tmp->leftChild)
{
tmp = tmp->leftChild;
}
return tmp;
}
//返回樹種最大的節點
template<typename T>
BSTNode<T>* BST<T>::MaxMum()
{
BSTNode<T>*tmp = root;
while (tmp->rightChild)
{
tmp = tmp->rightChild;
}
return tmp;
}
template<typename T>
BST<T>::~BST()
{
delTree(root);
}
template<typename T>
void BST<T>::delTree(BSTNode<T>*root)
{
BSTNode<T>*tmpLeft = NULL;
BSTNode<T>*tmpRight = NULL;
while (root)
{
tmpLeft = root->leftChild;
tmpRight = root->rightChild;
delete root;
delTree(tmpLeft);
delTree(tmpRight);
}
}
測試文件
#include"dm_04_BST.hpp"
#include"iostream"
#include"cmath"
using namespace std;
void main()
{
Element<int> a,b,c,d,e,f,g,h;
BST<int> tree;
a.key = 5; a.name = 'a'; cout << tree.BST_insert(a) << endl;
b.key = 2; b.name = 'b'; cout << tree.BST_insert(b) << endl;
c.key = 7; c.name = 'c'; cout << tree.BST_insert(c) << endl;
d.key = 1; d.name = 'd'; cout << tree.BST_insert(d) << endl;
e.key = 6; e.name = 'e'; cout << tree.BST_insert(e) << endl;
f.key = 8; f.name = 'f'; cout << tree.BST_insert(f) << endl;
g.key = 4; g.name = 'g'; cout << tree.BST_insert(g) << endl;
h.key = 0; h.name = 'h'; cout << tree.BST_insert(h) << endl;
//打印二叉樹,從根節點開始,遞歸打印左右
tree.display();
cout << "遞歸搜索Key" << endl;
cout<<tree.BST_Search(e)->getElement().key<<endl;
cout << "迭代搜索Key" << endl;
cout << tree.BST_Search_Iterator(e)->getElement().key<<endl;
cout << "搜索二叉樹中序遍歷恰好是從小到大" << endl;
tree.midOrder();
cout << "二叉搜索樹中鍵值最大的" << endl;
Element<int> max = tree.MaxMum()->getElement();
cout << "key: " << max.key << " name: " << max.name << endl;
cout << "二叉搜索樹中鍵值最小的" << endl;
Element<int> min = tree.MinMum()->getElement();
cout << "key: " << min.key << " name: " << min.name << endl;
cout <<endl<< "刪除節點d" << endl;
tree.Delete_Node(d);
tree.midOrder();
cout << endl;
system("pause");
}
測試運行結果
缺點:
二叉搜索樹,在一定條件下會退化爲鏈表,失去二叉搜索樹的優點,構建二叉搜索樹應當隨機構建二叉搜索樹,n個節點n!中排序,每一種排序等概。
平衡二叉樹紅黑樹可以解決這個問題,有時間在努力封裝一下紅黑樹吧。
有些頹廢,希望自己能珍惜時間。