二叉搜索树简要提点
- 二叉树(binary tree)是一种树型数据结构,其中它的每个节点最多有两个孩子(可以没有,也可以只有一个)。
- 二叉搜索树(binary search tree)是一种特殊的二叉树,对每个节点,其左孩子/左子树的关键字值该节点的关键字值;其右孩子/右子树的关键字值都大于该节点的关键字值。
- 二叉搜索树的平均深度为 O(logN) O ( log N ) 。其中, N N <script type="math/tex" id="MathJax-Element-2">N</script>为节点总数。
- 二叉搜索树的主要操作有:创建(初始化)二叉树,查找关键字Find,返回最大最小关键字FindMax, FindMin,向树中插入元素Insert,删除节点Delete,遍历二叉树。
- 涉及到二叉树的操作有两种方法实现:递归实现和非递归实现。
二叉搜索树及其相关操作
创建二叉搜索树
一、二叉(搜索)基本数据结构
- 树是由一个个节点构成,节点的构成成一般为:该节点包含的关键字值(data),left指针指向其左孩子,right指针指向其右孩子。
- 节点结构的一般声明如下:
typedef struct _TreeNode { // 定义二叉查找树的结构
ElemType data; // 数据
struct _TreeNode* left; // 指向左孩子指针
struct _TreeNode* right; // 指向其右孩子指针
}TreeNode, *SearchBTree; // TreeNode表示节点别称,*SearchBTree表示一个指向树(子树)根节点的指针
二、初始化树
- 一般初始化树为一个(TreeNode类型的)空指针。
- 初始化树可以和树的销毁工作合并成MakeEmpty()函数,递归地实现如下。
SearchBTree EmptyTree(SearchBTree T) // 初始化、构造一颗空树/销毁一颗树
{
if (!T)
{
EmptyTree(T->left); // 递归地释放空间
EmptyTree(T->right);
delete T;
}
return nullptr;
}
向二叉搜索树中插入元素
- 根据树的结构特点,比较要插入元素X值与节点关键字的大小,递归地插入(一般递归实现比较简单)。
- 当X值小于某个节点的值,则递归地往其左子树中插入X;当X值大于某个节点的值,则递归地往其右子树中插入X。
- 递归的终止条件是节点为空,申请一个新节点关键字为X插入到树中。
void Insert(SearchBTree &T, ElemType x)
{
if (!T)
{
TreeNode* pT = new TreeNode; // 申请节点空间
pT->data = x; // 为节点赋值
pT->left = pT->right = nullptr;
T = pT; // 将pT赋给T
}
else
{
if (x < T->data) // 如果x小于某个结点的数据
Insert(T->left, x); // 递归地在其左子树上寻找空结点插入
else if (x > T->data) // 如果x大于某个结点的数据
Insert(T->right, x); // 递归地在其左子树上寻找空结点插入
}
}
在二叉搜索树中查找
- 在树中查找某个关键字值,并返回其所在节点。
- 递归地进行:从根节点开始,当前节点大于其查找的值,则递归地再其左子树中查找。否则,在其右子树中递归地查找。
- 递归终止的条件是:查找到该关键字所在节点并返回。返回空结点,表示树中没有要查找的关键字值。
TreeNode* Find(SearchBTree T, ElemType x) // 查找树中是否有值x
{
if (T == nullptr) // 没有找到,返回空指针
return nullptr;
if (x < T->data) // 递归查找
return Find(T->left, x);
else if (x > T->data)
return Find(T->right, x);
else
return T; // 查找到了,则返回T指针
}
- 查找树中的最小值
- 1.递归实现:递归地查找其左子树。
TreeNode* FindMin(SearchBTree T) // 查找最小值,递归实现
{
if (T->left == nullptr)
return T;
else
return FindMin(T->left);
}
- 2.非递归实现:从根节点开始,循环遍历每个结点的左孩子,发现左孩子为空,则停止并返回该结点。
TreeNode* FindMin2(SearchBTree T) // 查找最小值,非递归实现
{
while (T->left != nullptr)
T = T->left;
return T;
}
- 查找树中的最大值
- 递归实现:递归地查找其右子树。
TreeNode* FindMax(SearchBTree T) // 查找最大值
{
if (T->right == nullptr)
return T;
else
return FindMax(T->right);
}
删除二叉搜索树中的某个节点
- 分两种情况:
- 1.删除的节点是树叶节点(没孩子):则直接删除。
- 2.删除的节点是(子树的)根节点:也分两种情况:① 该节点有一个孩子,则直接替换为其孩子节点,然后删除释放其自身。
- ② 该节点有两个孩子,先把该节点的关键字值替换为其右子树的最左边孩子的关键字值(最小值)。然后再删除其右子树自身的最小值。
void Delete(SearchBTree &T, ElemType x) // 删除指定元素
{
TreeNode *tmpNode = new TreeNode; // 创建一个临时结点
if (T == nullptr) // 没有x存在
cout << "404 Not Found\n";
else if (x < T->data) // x小于某个结点值
Delete(T->left, x); // 递归地从其左子树删除x
else if (x > T->data) // x大于某个结点的值
Delete(T->right, x); // 递归地从其右子树删除x
else // 找到了x
{
if (T->left&&T->right) // x所在结点有两个孩子的情况
{
tmpNode = FindMin(T->right); // 将临时结点赋值为其右子树中具有最小值的结点(即最左端的结点)
T->data = tmpNode->data; // 移动数据至要删除的目标结点
Delete(T->right, tmpNode->data); // 删除其右子树中值为x的结点
}
else
{ // x所在结点只有一个孩子和没有孩子的情况
tmpNode = T; // 要删除的结点T赋给临时结点tmpNode
if (T->left == nullptr) // 若当前结点的左孩子为空
T = T->right; // 则,将当前结点赋值为其右孩子
else if (T->right == nullptr) // 若当前结点的右孩子为空
T = T->left; // 则,将当前结点赋值为其左孩子
else
delete tmpNode; // 否则当前结点为叶子结点(没有孩子),则直接释放
}
}
}
递归遍历二叉搜索树
- 二叉搜索树主要有三种遍历方式:先根(序)遍历,中根(序)遍历,后根(序)遍历
- 1.先根遍历:先访问根节点,再递归地遍历其左子树,最后再递归地遍历其右子树
void PreorderPrint(SearchBTree T) // 先根(序)遍历二叉树并打印出结点值
{
if (T != nullptr)
{
cout << T->data << " ";
PreorderPrint(T->left);
PreorderPrint(T->right);
}
}
- 1.中根遍历:先递归地遍历其左子树,然后再访问根节点,最后再递归地遍历其右子树
void InorderPrint(SearchBTree T) // 中根(序)遍历二叉树并打印出结点值
{
if (T != nullptr)
{
InorderPrint(T->left);
cout << T->data << " ";
InorderPrint(T->right);
}
}
- 3.后根遍历:先递归地遍历其左子树,再递归地遍历其右子树,最后再访问根节点
void PostorderPrint(SearchBTree T) // 中根(序)遍历二叉树并打印出结点值
{
if (T != nullptr)
{
PostorderPrint(T->left);
PostorderPrint(T->right);
cout << T->data << " ";
}
}
相关操作及运行结果
C/C++主程序
int main()
{
const ElemType rawdata[] = { 19, 7, 9, 15, 23, 39, 4, 2, 75, 100, 43, 58 };
SearchBTree myTree = new TreeNode;
myTree = EmptyTree(myTree); // 初始化树
for (int i = 0;i < sizeof(rawdata) / sizeof(ElemType);i++)
{
Insert(myTree, rawdata[i]); // 向树中插入给定数据
}
cout << "The preorder print of the tree is: \n";
PreorderPrint(myTree);
cout << endl;
cout << "The inorder print of the tree is: \n";
InorderPrint(myTree);
cout << endl;
cout << "The postorder print of the tree is: \n";
PostorderPrint(myTree);
cout << endl;
cout << "Input a value: ";
ElemType x;
cin >> x;
if (Find(myTree, x))
cout << x << "is in the tree.\n";
else
cout << x << "is not in the tree.\n";
TreeNode* TmpNode = new TreeNode;
TmpNode = FindMin(myTree);
cout << "The minimum value in the tree is: " << TmpNode->data << endl;
TmpNode = FindMax(myTree);
cout << "The maximum value in the tree is: " << TmpNode->data << endl;
cout << "Input a value to delete: ";
cin >> x;
Delete(myTree, x);
cout << "Now, the inorder print of the tree is: \n";
InorderPrint(myTree);
cout << "\nDestroy the tree..." << endl;
EmptyTree(myTree);
system("pause");
return 0;
}
操作运行结果
The preorder print of the tree is:
19 7 4 2 9 15 23 39 75 43 58 100
The inorder print of the tree is:
2 4 7 9 15 19 23 39 43 58 75 100
The postorder print of the tree is:
2 4 15 9 7 58 43 100 75 39 23 19
Input a value to search: 13
13 is not in the tree.
The minimum value in the tree is: 2
The maximum value in the tree is: 100
Input a value to delete: 58
Now, the inorder print of the tree is:
2 4 7 9 15 19 23 39 43 75 100
Destroy the tree...