二叉搜索树(Binary Search Tree)也叫做二叉排序树或者二叉查找树。顾名思义,它是一种对排序和查找都很有用的特殊二叉树。
二叉查找树满足以下性质:(假设二叉查找树中每个节点元素都是不同的,它也可以为空)
- 非空左子树的所有键值小于其根节点的键值;
- 非空右子树的所有键值大于其根节点的键值;
- 左,右两棵子树都是二叉搜索树
二叉搜索树本质上还是一棵二叉树,只不过对它做了限定,就行队列和栈一样,它们是对线性表做了限制。对二叉搜索树的遍历和创建操作与普通二叉树一致。但是二叉搜索树的特点使得对它的查找,插入,删除变得有些不同。
二叉搜索树的平均深度是O(logn)的,一般不会造成爆栈的。
二叉搜索树的对于查找问题的解决,本质上还是二分法的使用。但是不同于我们对一个有序数组使用的二分查找法。有序数组上施加的二分查找是元素个数恒定不变的(不进行插入和删除操作),称之为静态查找。二叉搜索树则可以支持插入和删除操作,它使得查找的范围可以动态变化,称之为动态查找。
如果按照查找操作是如何进行的来分类,那么二叉搜索树和二分查找都是基于比较实现的;另外一种实现查找的方式是基于映射实现的,即:散列表,或者称之为哈希表。
二叉搜索树的ATD和操作集
#ifndef BST
#define BST
#include<iostream>
#include<stack>
using std::cout;
using std::cin;
using std::stack;
typedef struct TreeNode Tree;
typedef Tree * PTree;
typedef PTree Position;
typedef int ElementType;
struct TreeNode
{
ElementType data;
PTree left;
PTree right;
};
Position Find(ElementType k, PTree T); //递归版查找
Position Find(ElementType k, PTree T,int a); //迭代版查找
Position FindMax(PTree T); //返回最大元素
Position FindMin(PTree T); //返回最小元素
void InorderTraversal(PTree T); //递归版中序遍历
void InorderTraversal(PTree T,int a); //迭代版中序遍历
PTree Insert(ElementType x, PTree T); //插入操作
PTree Delete(ElementType x, PTree T); //删除操作
int PostinorderGetHeight(PTree T); //二叉树的高度
#endif // !BST
二叉搜索树操作集的C++实现代码:
#include "searchtree.h"
//递归版本实现的查找函数,二叉树的平均深度是O(log n),可以递归的
Position Find(ElementType k, PTree T)
{
if (NULL == T)
{
return NULL; //空树
}
if (k < T->data) //k小于data,去该节点的左子树查找。
{
return Find(k,T->left);
}
else if (k > T->data) //k大于data,去该节点的右子树查找。
{
return Find(k, T->right);
}
else if (k == T->data) //k等于data,直接返回该节点。
{
return T;
}
}
Position Find(ElementType k, PTree T, int a)
{
while (T) //树不空执行循环
{
if (k > T->data)
{
T = T->right; //转右子树查找
}
else if (k < T->data)
{
T = T->left; //转左子树查找
}
else
{
return T; //找到了该元素
}
}
return NULL; //没有找到该元素
}
Position FindMax(PTree T)
{
if (NULL == T)
{
return NULL; //空树
}
else
{
while (T->right)
{
T = T->right; //最大值在其右子树上,且该右子树无右子树
}
return T; //返回最大值所在节点
}
}
Position FindMin(PTree T)
{
if (NULL == T)
{
return NULL; //空树
}
else
{
while (T->left)
{
T = T->left; //最小值在其左子树上,且该左子树无左子树
}
return T; //返回最小值所在节点
}
}
void InorderTraversal(PTree T)
{
if (T)
{
InorderTraversal(T->left);
cout << T->data << ' ';
InorderTraversal(T->right);
}
}
void InorderTraversal(PTree T, int a)
{
if (NULL == T)
{
return;
}
else
{
stack<PTree> S; //创建空栈
while (T || !S.empty())
{
while (T)
{
S.push(T); //将T压入栈中
T = T->left; //转左子树
}
if(!S.empty())
{
/*T = (PTree)malloc(sizeof(Tree));
T->data = S.top()->data;
T->left = S.top()->left;
T->right = S.top()->right;*/
T = S.top();
cout << T->data << ' ';
T = T->right; //转右子树
S.pop(); //删除栈顶元素
}
}
}
}
PTree Insert(ElementType x, PTree T)
{
PTree temp = NULL;
PTree BT = T;
if (!T)
{
T = (PTree)malloc(sizeof(Tree));
T->data = x;
T->left = T->right = NULL;
return T;
}
else
{
while (T)
{
if (x < T->data)
{
if (!T->left) //若左子树为空,插入到这里
{
temp = (PTree)malloc(sizeof(Tree));
temp->data = x;
temp->left = NULL;
temp->right = NULL;
T->left = temp;
break;
}
else //若不空,转左子树
{
T = T->left;
}
}
if (x > T->data)
{
if (!T->right) //若右子树为空,插入到这里
{
temp = (PTree)malloc(sizeof(Tree));
temp->data = x;
temp->left = NULL;
temp->right = NULL;
T->right = temp;
break;
}
else //若不空,转右子树
{
T = T->right;
}
}
if (x == T->data) //找到这个元素
{
break;
//如果要插入的元素已经在该树中存在,那么一般有两种做法
//1.什么操作也不做,适合在不追求重复元素的场合使用
//2.向树的ADT中增加一个域,用来保存该元素的出现次数。这样使得树
//增加了一下附加空间,但是,却比将重复信息放入树中好的多,这样避免
//了树深度的增加。
//在这里我们采用第一种方式,什么也不做。
}
}
return BT;
}
}
PTree Delete(ElementType x, PTree T)
{
PTree temp;
if (!T)
{
cout << "删除元素未找到!
";
}
else
{
if (x < T->data)
{
T->left = Delete(x, T->left); //递归删除左子树
}
else if (x > T->data)
{
T->right = Delete(x, T->right); //递归删除右子树
}
else
{
if (T->left && T->right) //左右子树都存在
{
temp = FindMin(T->right);
//temp = FindMax(T->left);
T->data = temp->data;
T->right = Delete(T->data, T->right);
}
else
{
temp = T;
if (!T->left) //右子树存在或者左右子树都不存在
{
T = T->right;
}
else if (!T->right) //左子树存在或者左右子树都不存在
{
T = T->left;
}
free(temp);
}
return T;
}
}
}
int PostinorderGetHeight(PTree T)
{
if (T)
{
int l, r, max;
l = PostinorderGetHeight(T->left);
r = PostinorderGetHeight(T->right);
max = l > r ? l : r;
return max + 1;
}
else
{
return 0;
}
}
main.cpp文件
#include"searchtree.h"
int main()
{
PTree BT = NULL;
for (int i = 5; i < 10; i++) //把根节点设置为5,插入5-9;
{
BT = Insert(i, BT);
}
for (int i = 0; i < 5; i++) //插入0-4.
{
BT = Insert(i, BT);
}
Insert(5, BT);
//理论上中序遍历输出应该是0 1 2 3 4 5 6 7 8 9
cout << "中序遍历输出:";
InorderTraversal(BT,1);
cout << "
树的深度是:";
cout << PostinorderGetHeight(BT);
cout << "
树中最大元素是:" << FindMax(BT)->data;
cout << "
树中最小元素是:" << FindMin(BT)->data;
cout << '
' << "删除元素5后,中序遍历结果:";
BT = Delete(5, BT);
InorderTraversal(BT);
cout << '
';
PTree temp;
cout << "查找5这个元素的结果:";
temp = Find(5, BT);
if (NULL == temp)
{
cout << "未找到!
";
}
else
{
cout << temp->data << '
';
}
cout << "查找3这个元素的结果:";
temp = Find(3, BT);
if (NULL == temp)
{
cout << "未找到!
";
}
else
{
cout << temp->data << '
';
}
system("pause");
return 0;
}
运行结果: