二叉查找树是这样定义的:
二叉查找树(Binary Search Tree),或者是一棵空树,或者是具有下列性质的二叉树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树
二叉查找树是一种高效的搜寻树,类似于如下:
以下是我参考网上的资源实现的:
#include <iostream>
using namespace std;
/*二叉查找树的节点类*/
template <class T>
class TreeNode
{
public:
TreeNode():left(NULL),right(NULL),freq(1){}
T data;
unsigned int freq;
TreeNode *left;
TreeNode *right;
};
/*二叉查找树类*/
template <class T>
class BST
{
public:
BST():root(NULL){}
void insert(T x);//提供的公共接口
void traversal();
TreeNode<T> *find(T x);
void Delete(T x);
private:
TreeNode<T> *root;
void insert_node(TreeNode<T>* &node, T x);
void InOrder(TreeNode<T> *node);
TreeNode<T> *findnode(TreeNode<T> *node, T x);
void delete_node(TreeNode<T> *&node, T x);
};
template<class T>
void BST<T>::insert_node(TreeNode<T>* &node, T x)
{
if(node == NULL)
{
node = new TreeNode<T>();
node->data = x;
return;
}
if(node->data > x)
insert_node(node->left, x);
else if(node->data < x)
insert_node(node->right, x);
else
(node->freq)++;//频数加1
}
template<class T>
void BST<T>::insert(T x)
{
insert_node(root, x);
}
template<class T>
void BST<T>::traversal()
{
InOrder(root);
}
template <class T>
void BST<T>::InOrder(TreeNode<T> *node)
{
if(node != NULL)
{
InOrder(node->left);
cout<<node->data<<endl;
InOrder(node->right);
}
}
template <class T>
TreeNode<T>* BST<T>::find(T x)
{
return findnode(root, x);
}
template <class T>
TreeNode<T>* BST<T>::findnode(TreeNode<T>* node, T x)
{
if(node == NULL) return NULL;//类似于二分法查找
if(node->data == x)
return node;
else if(node->data < x)
return findnode(node->right, x);
else
return findnode(node->left, x);
}
template<class T>
void BST<T>::Delete(T x)
{
delete_node(root, x);
}
template<class T>
void BST<T>::delete_node(TreeNode<T> *&node, T x)
{
if(node == NULL) return;
if(node->data > x)
delete_node(node->left, x);
else if(node->data < x)
delete_node(node->right, x);
else
{
if(node->left != NULL && node->right != NULL)//有两个孩子节点
{
TreeNode<T>*temp = node->right;
while(temp->left != NULL) temp = temp->left;//找到右子树中最小的孩子节点
node->data = temp->data;
node->freq = temp->freq;
delete_node(node->right, node->data);//删除右子树中最小的孩子节点
}
else//有1个或者0个孩子节点
{
TreeNode<T>* temp = node;
if(node->left == NULL)
node = node->right;
else if(node->right == NULL)
node= node->left;
}
}
}
int main()
{
int a[] = {3,2,5,6,4,1};
BST<int> *bst = new BST<int>();
for(int i = 0; i < sizeof(a)/sizeof(a[0]); i++)
bst->insert(a[i]);
bst->traversal();
TreeNode<int> *node = bst->find(a[3]);
if(node != NULL)
cout<<"find the data "<<node->data<<endl;
else
cout<<"can't find the data"<<endl;
bst->Delete(a[0]);
cout<<"after delete "<<a[0]<<endl;
bst->traversal();
return 0;
}
下面对算法进行解释:
定义BST的时候我们使用了一个私有的接口和一个公有的接口,是一个小技巧,体现了C++的封装性
insert函数:
想要构造一个二叉查找树,每一次都是把一个节点插入到现有的二叉查找树中,在插入的过程中,递归比较,如果比该节点大就往右插入,否则往左插入
traversal()函数:
中序遍历二叉树能够得到一个有序的数列,因为我们二叉查找树就是定义成node->left->data i小于node->data小于node->right->data,如果按照中序遍历自然是有序的了
delete()函数:
删除一个节点比较复杂,因为要考虑到这个节点是否有孩子,以及孩子的个数,可以分类来考虑,其实不难:
1.如果没有孩子节点,直接删除这节点就是了
2.如果有一个孩子节点,就用这个孩子节点来代替这个节点
3.如果有两个孩子节点,约定俗成的用右子树中最小的节点来代替这个节点,其实就是右子树的最最左子节点y
二叉查找树有其弊端,我们说其查找,插入的时间复杂度是
O(logn),但是但我们的测试数组是{1,2,3,4,5,6}的时候,构造的二叉查找树就是这样的了
1
2
3
4
5
6
那么我们的搜寻时间就变为了O(n)线性的了,这个就不能体现它的优势了,所以我们可以优化,优化的方法可以有红黑树和AVL树等