二叉搜索树支持多种动态集合操作。treeSearch、treeMin、treeMax、successorTree(后继)、predecessorTree(前驱)、insert、delete
一棵二叉查找树是按二叉树结构来组织的。用链表来表示二叉查找树。结点的结构如下。
二叉搜索树的关键字的性质:设x为二叉搜索树的一个结点。如果y是x左子树中的一个结点,则y.val<=x.val;如果y是x右子树中的一个结点,则y.val>=x.val。
作用于二叉搜索树上的基本操作的时间与树的高度成正比,一棵含有n个结点的完全二叉树,这些操作的最坏情况运行时间为O(lgn)。
template <class T>
struct listNode
{
T val;
listNode *parent;
listNode *left;
listNode *right;
listNode(T & k) :val(k) {
parent = NULL;
left = NULL;
right = NULL;
}
};
由于二叉搜索树关键字的性质,这里采用中序遍历的方法来输出二叉树。
void inorderTree() {
if (firstNode != NULL) {
inorderTree(firstNode->left);
cout << firstNode->val;
inorderTree(firstNode->right);
}
}
在二叉搜索树中查找关键字等于k的结点
listNode* treeSearch(const T &k) {
if (firstNode == NULL || firstNode->val == k) {
return firstNode;
}
if (firstNode->val < k)
firstNode = firstNode->right;
else
firstNode = firstNode->left;
treeSearch(firstNode);
}
//非递归的方式
listNode* iterativeSearch(const T &k) {
while (firstNode&&firstNode->val!=k) {
if (firstNode->val < k)
firstNode = firstNode->right;
else
firstNode = firstNode->left;
}
return firstNode;
}
找出以某个结点为根结点的二叉搜索树的最大值和最小值
listNode* treeMin(listNode* x) {
listNode* minNode;
while (x)
{
minNode = x;
x = x->left;
}
return minNode;
}
listNode* treeMax(listNode* x) {
listNode* maxNode;
while (x)
{
maxNode = x;
x = x->right;
}
return maxNode;
}
找出某个结点的前驱结点与后继结点
//后继结点
listNode* successorTree(listNode* x){
//如果x有右子树,则其后继就是右子树中的最左结点,也就是求右子树的最小结点。
if (x->right != NULL) {
return treeMin(x->right);
}
//如果x没有右子树,且x有后继y,则y是x的最低的一个祖先结点,且y的左孩子也是x的
//祖先结点。
//可从x向上查找,直至遇到某个是其父结点的左儿子的结点为止。同理可推出求x前驱的方法
listNode* y = x->parent;
while (y&&y->right=x) {
x = y;
y = y->parent;
}
if (y == NULL) return NULL;
return y;
}
//前驱结点
listNode* predecessorTree(listNode* x) {
if (x->left != NULL) {
return treeMax(x->left);
}
listNode* y = x->parent;
while (y&&y->left = x) {
x = y;
y = y->parent;
}
if (y == NULL) return NULL;
return y;
}
插入结点
void insertTree(listNode* z) {
listNode* pNode = NULL;
//遇到val值相同的结点,将z放到该结点的右子树上
while (firstNode) {
pNode = firstNode;
if (firstNode->val > z->val) {
firstNode = firstNode->left;
}
else
firstNode = firstNode->right;
}
//此时该二叉搜索树为空
if (pNode == NULL) firstNode = z;
else if (firstNode == NULL) {
if (pNode->val > z->val)
pNode->left = z;
else pNode->right = z;
}
}
删除结点。从一棵二叉搜索树上删除一个给定的结点z的整个策略分为三种基本情况:
- 如果z没有孩子结点,那么只需简单地删除z结点,修改其父结点,用NULL来代替z
- 如果z只有一个孩子,那么将这个孩子提升到树中z的位置,修改其父结点,用z的孩子来代替z
如果z有两个孩子,那么找z的后继y(一定在z的右子树上),并让y占据树中z的位置
这里的处理过程综合前面的三种基本情况:
如果z没有左孩子,那么用右孩子来代替z,这里右孩子可以是NULL(此情况就是z没有孩子结点),也可以不是。
- 如果z仅有一个孩子且为其左孩子,那么用左孩子来替换z
- z既有左孩子又有右孩子。此时我们用z的后继y来代替z。y一定在z的右子树中,且y没有左孩子。如果y是z的右孩子,则用y代替z
- 如果y不是z的右孩子,则用y的右孩子来代替y,将y移除原来位置,成为z的右孩子的父结点,然后替代z,最后修改z的父结点和z的左孩子
//定义一个子过程,该函数是用另一棵子树替换一个子树并成为其双亲的孩子结点
void transplantTree(listNode *u, listNode *v) {
if (u->parent == NULL) firstNode = v;
else if (u->parent->left == u) u->parent->left = v;
else u->parent->right = v;
//这里允许v结点为NULL,当其不为NULL时,修改其父结点
if (v != NULL)
v->parent = u->parent;
return;
}
void deleteTree(listNode* z) {
//z结点只有左子树不为空,将左子树的根结点代替z
if (z->left != NULL&&z->right == NULL) {
transplantTree(z, z->left);
}
//z结点只有右子树不为空,将右子树的根结点代替z
else if (z->left == NULL&&z->right != NULL) {
transplantTree(z, z->right);
}
//z结点有左子树也有右子树,需要找出z的后继
else {
listNode* y = treeMin(z->right);
//如果z的后继正好是z的右子树,则用y代替z
if (y->parent == z) transplantTree(z, z->right);
//y不是z的后继,将y的右子树移到y的位置(因为y是z的后继,所以y没有左子树),将y代替z的位置
else
{
transplantTree(y, y->right);
y->right = z->right;
y->right->parent = y;
transplantTree(z, y);
y->left = z->left;
y->left->parent = y;
}
}
delete z;
}