数据结构之 二叉查找树
1. 二叉查找树的定义
二叉查找树(binary search tree)是一棵二叉树,或称为二叉搜索树,可能为空;一棵非空的二叉查找树满足一下特征:
- 每个元素有一个关键字,并且任意两个元素的关键字都不同;因此,所有的关键字都是唯一的。
- 在根节点的左子树中,元素的关键字(如果存在)都是小于根节点的关键字。
- 在根节点的右子树中,元素的关键字(如果存在)都是大于根节点的关键字。
- 根节点的左、右子树也都是二叉查找树。
2. 二叉查找树的操作和实现
二叉查找树是基于一个二叉树满足以上特征所实现的,因此关于二叉树的操作实现的博文:二叉树(C语言实现)
2.1 二叉查找树的抽象数据类型
typedef int myType;
typedef struct treeNode
{
myType element; //值域元素
struct treeNode *lchild; //左子树
struct treeNode *rchild; //右子树
}searchTree;
2.1 查找
递归查找关键字为data的元素,时间复杂度为O(h),h是树的高度
searchTree *find(myType data, searchTree *T)
{
if(T == NULL)
return NULL;
if(data < T->element) //如果小于根节点值则去左孩子查找
return find(data, T->lchild);
else if(data > T->element)//如果大于根节点值则去左孩子查找
return find(data, T->rchild);
else
return T;
}
2.2 查找最小值(递归实现)
根据二叉查找树的性质,遍历左子树。
searchTree *findMin(searchTree *T)
{
if(T == NULL)
return NULL;
else if(T->lchild == NULL)
return T;
else
return findMin(T->lchild);
}
2.3 查找最大值(非递归实现)
根据二叉查找树的性质,遍历右子树。
searchTree *findMax(searchTree *T)
{
if(T != NULL)
while(T->rchild != NULL)
T = T->rchild;
return T;
}
2.4 插入
如果data不存在,则插入,根据二叉查找树的性质,如果大于根节点元素,进入右子树,如果小于根节点元素则进入左子树。
searchTree *insert(myType data, searchTree *T)
{
if(T == NULL) {
T = (searchTree *)malloc(sizeof(struct treeNode));
T->element = data;
T->lchild = NULL;
T->rchild = NULL;
} else if (data < T->element) {
lchild = insert(data, T->lchild);
} else if (data > T->element) {
rchild = insert(data, T->rchild);
}
return T;
}
2.5 删除
删除操作有三种情况
- T是叶子节点。直接释放该节点的空间,如果是根节点,则直接赋值为NULL。
- T有一棵非空子树。如果是根节点,则T的唯一子树成为根节点,如果非根节点,则使其父节点指针绕过该节点指向其唯一子节点,释放空间。
- T有两棵非空子树。一般策略是将该节点的元素替换成它的左子树的最大元素或右子树的最小元素(代码中采用的是后者,即用右子树的最小元素替换被删除的节点),然后在删除被替换的元素。
searchTree *remove(myType data, searchTree *T)
{
searchTree *tmpNode;
if(T == NULL) {
printf("NOT FOUNT\n");
} else if (data < T->element) {
T->lchild = remove(data, T->lchild);
} else if (data > T->element) {
T->rchild = remove(data, T->rchild);
//找到该元素,开始删除
} else if (T->lchild && T->rchild){
//有两个子树的情况
tmpNode = findMin(T->rchild);
T->element = tmpNode->element;
T->rchild = remove(T->element, T->rchild);
} else {
//有一个或没有子树的情况
tmpNode = T;
if(T->lchild == NULL)
T = T->rchild; //绕过被删除的节点
else if(T->rchild == NULL)
T = T->lchild;
free(tmpNode);
}
return T;
}
//以上代码在删除有两个子树的情况效率不高,因为查找和删除右子树中最小的节点遍历了两趟。
以上关于二叉查找树的操作时间复杂度均为O( log2N )。