一颗二叉搜索树是以一颗二叉树来组织的。除了数据之外,每个结点还包含属性left,right和p,它们分别指向结点的左孩子,右孩子和双亲。如果某个孩子结点和父结点不存在,则相应属性值为NULL。根结点是树中唯一父指针为NULL的结点。
二叉搜索树对任何结点x,其左子树中的关键字最大不超过x.key,其右子树中的关键字最小不会低于x.key。
二叉搜索树的性质允许我们通过一个简单的递归算法来按序输出二叉搜索树中的所有关键字,这种算法称为中序遍历(inorder tree walk)算法。这样命名的原因是输出的子树根的关键字位于其左子树的关键字和右子树的关键字之间。(类似地,先序遍历(preorder tree walk)中输出的根的关键字在其左右子树的关键字之前,而后序遍历(postorder tree walk)输出的根的关键字在其左右子树之后。) 调用下面的中序遍历就可按升序输出一颗搜索二叉树中的所有元素,时间消耗为。
伪代码:
INORDER_TREE_WALK( x )
if x != NULL
INORDER_TREE_WALK( x.left )
print x.key
INORDER_TREE_WALK( x.right )
快速排序与二叉搜索树排序很相似:
查找
我们使用下面的过程在一颗二叉搜索树中查找一个具有给定关键字的结点。输入一个指向树根的指针和关键字k,如果这个结点存在,TREE-SEARCH返回一个指向关键字为k的结点的指针;否则返回NULL。
递归伪代码:
TREE_SEARCH(x,k)
if x== NULL or k==x.key
return x
if k<x.key
return TREE_SEARCH(x.left,k)
else
return TREE_SEARCH(x.right,k)
迭代伪代码:
ITERATIVE_TREE_SEARCH(x,k)
while x!= NULL and k!=x.key
if k<x.key
x=x.left
else
x=x.right
return x
看图:
最大关键字元素和最小关键字元素
通过从树根开始沿着left孩子指针直到遇到一个NULL,我们总能在二叉搜索树中找到最小的元素,同理我们从树根开始沿着right孩子指针直到遇到NULL,我们总能在二叉搜索树中找到最大的元素。
找最小元素
TREE_MINIMUM(x)
while x.left != NULL
x=x.left
return x
找最大元素
TREE_MAXMUM(x)
while x.right !=NULL
x=x.right
return x
后继和前驱
给定一颗二叉搜索树中的一个结点,有时候需要按中序遍历的次序查找它的后继。如果所有的关键字互不相同,则一个结点x的后继是大于x.key的最小关键字的结点。一颗二叉搜索树的结构允许我们通过没有任何关键字的比较来确定一个结点的后继。如果后继存在,下面的过程将返回一颗二叉搜索树中的结点x的后继;如果x是这颗树中的最大关键字,则返回NULL。
TREE_SUCCESSOR(x)
if x.right != NULL //如果右子树不为空
return TREE_MINIMUM(x.right) //则后继为右子树的最左结点(最小结点)
y=x.p //右子树为空,令y为x的父结点
while y!=NULL and x == y.right //若y不为空且x是y的右节点,即x是y的后继
x = y
y = y.p
return y
看图:
查找算法分析:
查找的平均时间消耗为高度时间,即
注意:
当有一排好序的数组(升序逆序都可)构建二叉搜索树会出现
此时查找的时间为,解决该问题需要引入平衡二叉树(二叉搜索树的优化版)。
插入
要将一个新值v插入到二叉搜索树T中,需要调用TREE_INSERT。该过程一结点z作为输入,z.key = v,从根开始比较,小就向左孩子比较,大就向右孩子比较,直到找到空位置。
TREE_INSERT(T,z)
y= NULL
x= T.root
while(x!=NULL)
y=x
if z.key <x.key
x=x.left
else
x=x.right
z.p=y
if y==NULL //树为空,z为头结点
T.root=z
else if z.key<y.key //判断是左孩子还是右孩子
y.left =z
else y.right = z
看图:
插入算法分析
在一颗均匀的高度为h的二叉树上插入一个结点消耗的时间为O(h)=O(),那么插入n个结点(非有序的)消耗的时间为O(n*
)。
删除
代码部分;
#include<stdio.h>
#include<stdlib.h>
typedef int DataType;
typedef struct BSTNode
{
DataType data;
struct BSTNode* left;
struct BSTNode* right;
struct BSTNode* parent;
}Node;
typedef struct BSTree
{
Node* root=NULL;
}BST;
//插入
void Tree_Insert(BST &T, DataType x)
{
if (T.root == NULL) //树空,插入到根结点
{
T.root = (Node*)malloc(sizeof(Node));
T.root->left = NULL;
T.root->right = NULL;
T.root->data = x;
return;
}
Node* cur, * pre; //找结点位置
cur = T.root;
pre = NULL;
while (cur)
{
pre = cur;
if (cur->data > x)
{
cur = cur->left;
}
else
{
cur = cur->right;
}
}
if (pre->data > x) //判断在其父结点的左还是右
{
pre->left= (Node*)malloc(sizeof(Node));
pre->left->data = x;
pre->left->parent = pre;
pre->left->left = NULL;
pre->left->right = NULL;
}
else
{
pre->right = (Node*)malloc(sizeof(Node));
pre->right->data = x;
pre->right->parent = pre;
pre->right->left = NULL;
pre->right->right = NULL;
}
}
//查找
bool Tree_Search(BST& T, DataType x)
{
Node* cur = T.root;
while (cur && cur->data != x)
{
if (cur->data > x)
{
cur = cur->left;
}
else
{
cur = cur->right;
}
}
if (cur)
return true;
return false;
}
//找最大值
Node* Tree_Maxmum(Node* _root)
{
Node* root = _root;
while (root->right)
{
root = root->right;
}
return root;
}
//接替位置
void Transplant(BST& T, Node* u, Node* v)
{
if (u== T.root) //是根结点
{
T.root = v;
}
else if (u->parent->left == u)
{
u->parent->left = v;
}
else
{
u->parent->right = v;
}
if(v)
v->parent = u->parent;
}
//删除
void Tree_Delete(BST& T, DataType x)
{
Node* cur = T.root;
while (cur && cur->data != x)
{
if (cur->data > x)
{
cur = cur->left;
}
else
{
cur = cur->right;
}
}
if (cur->left == NULL)
{
Transplant(T,cur, cur->right);
free(cur);
}
else if (cur->right == NULL)
{
Transplant(T, cur, cur->left);
free(cur);
}
else
{
Node* y = Tree_Maxmum(cur->left);
if (y->parent != cur)
{
Transplant(T, y, y->left);
y->left = cur->left;
cur->left->parent = y;
}
Transplant(T, cur, y);
y->right = cur->right;
cur->right->parent = y;
free(cur);
}
}
//中序遍历
void Inorder(Node*root)
{
Node* cur = root;
if (cur == NULL)
return;
Inorder(cur->left);
printf("%d ", cur->data);
Inorder(cur->right);
}
int main()
{
BST T;
Tree_Insert(T, 7);
Tree_Insert(T, 3);
Tree_Insert(T, 9);
Tree_Insert(T, 1);
Tree_Insert(T, 6);
Tree_Insert(T, 8);
Tree_Insert(T, 10);
Tree_Insert(T, 2);
Tree_Insert(T, 4);
Tree_Insert(T, 5);
Inorder(T.root);
printf("\n");
if (Tree_Search(T, 5))
printf("找到了5\n");
else
printf("没找到\n");
Tree_Delete(T, 4);
Inorder(T.root);
printf("\n");
Tree_Delete(T, 7);
Inorder(T.root);
printf("\n");
Tree_Delete(T, 5);
Inorder(T.root);
printf("\n");
Tree_Delete(T, 8);
Inorder(T.root);
printf("\n");
}
输出:
1 2 3 4 5 6 7 8 9 10
找到了5
1 2 3 5 6 7 8 9 10
1 2 3 5 6 8 9 10
1 2 3 6 8 9 10
1 2 3 6 9 10