前言
二叉查找树(Binary Search Tree,又叫二叉搜索树,二叉排序树)是这样的一种数据结构:
- 它或者是一棵空树,或者是具有下列性质的二叉树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树。
BST被称为二叉排序树的原因就在于树中所有的元素都可以用某种统一的方式排序(假设运算符“<”,“>”,“=”可以应用于树中元素)。
BST一般以二叉链表为其存储结构。
定义
头文件中的声明如下:
#ifndef TREES_H_INCLUDED
#define TREES_H_INCLUDED
// Binary Search Tree
struct TreeNode;
typedef struct TreeNode *Position;
typedef struct TreeNode *BST;
#define ElementType int
BST MakeEmpty(BST T);
BST InitialTree();
Position Find(ElementType X, BST T);
Position FindMin(BST T);
Position FindMax(BST T);
BST Insert(ElementType X, BST T);
BST Delete(ElementType X, BST T);
ElementType Retrieve(Position P);
#endif // TREES_H_INCLUDED
结构体:
struct TreeNode {
ElementType Element;
BST Left, Right;
};
操作
查找
Find操作返回指向树T中具有Key值X的节点的指针。由于BST具有特殊的性质,所以这个操作可以比较简便地递归实现。
Position Find(ElementType X, BST T) {
if (T == NULL)
return NULL;
if (X < T->Element)
return Find(X, T->Left); //if X < T->Element, X must be in the left subtree of T.
else if (X > T->Element)
return Find(X, T->Right); //if X > T->Element, X must be in the right subtree of T.
else // X == T->Element
return T;
}
这样一来,可以自然而然的给出查找树中最小元素和最大元素的操作:
Position FindMin(BST T) {// using recursion
if (T == NULL)
return NULL;
else if (T->Left == NULL)
return T;
else
return FindMin(T->Left);
}
Position FindMax(BST T) {// using loop
if (T != NULL) {
while (T->Right != NULL)
T = T->Right;
}
return T;
}
插入
插入操作并不难想象,为了将X插入树T,可以用Find()一样的方法沿着树查找,如果成功Find到了X,则什么都不用做。(因为此时X已在树中)否则,将X插入到所遍历的路径上的最后一点上(显然,插入后的新树仍然是合格的BST)。
总之,插入操作满足这样的规定:
1. 若当前的二叉查找树为空,则插入的元素为根节点。
2. 若插入的元素值小于根节点值,则将元素插入到左子树中。
3. 若插入的元素值不小于根节点值,则将元素插入到右子树中。
BST Insert(ElementType X, BST T) {
if (T == NULL) {
T = malloc(sizeof(T));
if (T == NULL) {
printf("Out of space!\n");
} else {
T->Element = X;
T->Left = T->Right = NULL;
}
}
else if (X < T->Element)
T->Left = Insert(X, T->Left);
else if (X > T->Element)
T->Right = Insert(X, T->Right);
// else X is in the tree already, we will do nothing.
return T;
}
删除
删除操作相对比较麻烦,需要分几种情况来看:
- 节点是叶子节点,则可以直接删除。
- 节点有一个子节点(即只有左子树或右子树),则将该节点的子节点和父节点相连(看上去像是绕过了该节点),然后删除该节点。
- 该节点有两个子节点(左右子树都有),一般的方法:用其右子树的最小的元素代替该节点的数据并递归地删除该最小节点(要注意到右子树的最小元素节点不可能有左子树)。
- 如果删除的次数不多,还会使用懒惰删除这样的策略。删去元素时节点仍留在树中,做一个已被删除的记号。
代码如下:
BST Delete(ElementType X, BST T) {
Position Tmp;
if (T == NULL) {
printf("Element not found!\n");
} else if (X < T->Element) {
T->Left = Delete(X, T->Left);
} else if (X > T->Element) {
T->Right = Delete(X, T->Right);
} else if (T->Left && T->Right) {// now we find the node X, it has two children
//replace with smallest one in right subtree
Tmp = FindMin(T->Right);
T->Element = Tmp->Element;
T->Right = Delete(T->Element, T->Right);
} else {// one or no children
Tmp = T;
if (T->Left == NULL)
T = T->Right;
else if (T->Right == NULL)
T = T->Left;
free(Tmp);
}
return T;
}