二叉搜索树
1. 什么是二叉搜索树
二叉搜索树(BST,Binary Search Tree)有以下性质:
- 非空左子树的所有键值小于根节点的键值
- 非空右子树的所有键值大于根节点的键值
- 左右子树都是二叉搜索树
2. 二叉搜索树的主要操作
2.1 Position Find
查找元素 X, 返回结点地址。下面给出算法思路:
- 如果树为空,返回 NULL
- 树不空,比较根结点关键字和 X
X 小于根节点键值,则只在左子树中搜索;
X 大于根节点键值,则只在右子树中搜索;
若两者结果相等,则搜索结束,返回指向结点的指针。算法的查找效率取决于树的高度。
下面给出两种实现方式,递归的方式和非递归的方式。
Position Find(ElementType X, BinTree BST)
{
if(!BST) return NULL;
if(X > BST->val) // 去右子树查找
return Find(X, BST->right); // 尾递归
else if(X < BST->val) // 去左子树查找
return Find(X, BST->left);
else
return BST; // X == BST->val 找到了
}
这种方法存在尾递归,算法编译效率不高。理论上,尾递归都可以用循环来实现。下面给出一种非递归的方法。
Position IterFind(ElementType X, BinTree BST)
{
while(BST)
{
if(X > BST->val)
BST = BST->right; // 向右子树移动查找
else if(X < BST->val)
BST = BST->left; // 向左子树移动查找
else
return BST; //找到了
}
return NULL; //没找到
}
2.2 Position FindMin
查找并返回最小元素所在结点地址。
最小元素一定在树的最左分支的端结点上。下面给出最小元素的递归函数。
Position FindMin(BinTree BST)
{
if(!BST) return NULL;
else if(!BST->left)
return BST; //找到最左叶结点
else
return FindMin(BST->left); //沿左分支继续查找
}
下面给出非递归的迭代算法。
Position FindMin(BinTree BST)
{
if(BST)
while(BST->left)
BST = BST->left; //一直沿左分支查找,直至最左叶子结点
return BST;
}
2.3 Position FindMax
查找并返回最大元素所在结点地址。
最大元素一定在树的最右分支的端结点上。下面给出最大元素的递归函数。
Position FindMax(BinTree BST)
{
if(!BST) return NULL;
else if(!BST->right)
return BST; //找到最右叶结点
else
return FindMin(BST->right); //沿右分支继续查找
}
下面给出非递归的迭代算法。
Position FindMax(BinTree BST)
{
if(BST)
while(BST->right)
BST = BST->right; //一直沿右分支查找,直至最右叶子结点
return BST;
}
2.4 BinTree Insert
插入的关键在于找到元素应该插入的位置,可以借鉴 find 函数。不同之处是需要记住插入结点后的根结点位置(即插入结点所挂在的树结点位置,也可以简单理解为插入结点的前一个结点)。
BinTree Insert(ElementType X, BinTree BST)
{
if(!BST) // 树为空,生成并返回一个结点的二叉搜索树(指针)
{
BST = malloc(sizeof(struct TreeNode));
BST->val = X;
BST->left = BST->right = NULL;
}
else // 树不为空,查找插入元素的位置
{
if(X < BST->val)
{
BST->left = Insert(X, BST->left); //递归插入左子树
//将上述语句改为 return Insert(X, BST->left); 则和 find 函数相同
}
else if(X > BST->val)
{
BST->right = Insert(X, BST->right); //递归插入右子树
//将上述语句改为 return Insert(X, BST->right); 则和 find 函数相同
}
}
return BST; //X已经存在,不做任何操作
}
2.5 BinTree Delete
删除结点,首先要找到结点。如果要删除的是叶结点,则直接删除。并把父结点指针置为NULL。如果要删除的结点只有一个孩子结点,就将要删除的结点的父结点指针指向要删除结点的孩子指针。
最复杂的情况是要删除的结点有左右两颗子树。这种情况下,要用一个结点替代被删除结点。右子树的最小元素或者左子树的最大元素。这种方法的本质是将删除有两个子树的结点转换为删除只有一个孩子的结点或者没有孩子的结点的问题。
注意:左子树的最大元素在左子树的最右边,所以没有右孩子。
右子树的最小元素在右子树的最左边,所以没有左孩子。
BinTree Delete(ElementType X, BinTree BST)
{
Position Tmp;
if(!BST) printf("要删除的元素未找到");
else if(X < BST->val)
{
BST->left = Delete(X, BST->left);
}
else if(X > BST->Data)
{
BST->right = Delete(X, BST->right);
}
else
{
if(BST->left && BST->right)
{
Tmp = FindMin(BST->right);
BST->val = Tmp->val;
BST->right = Delete(BST->val, BST->right);
}
else
{
Tmp = BST;
if(!BST->left)
{
BST = BST->right;
}
else if(!BST->right)
{
BST = BST->left;
}
free(Tmp);
}
}
return BST;
}