定义
二叉排序树,又称二叉查找树 (BST)
一颗二叉树或者空二叉树,或者是具有如下性质的二叉树:
- 左子树上所有的结点的关键字均小于根节点的关键字。
- 右子树上所有的结点的关键字均大于根节点的关键字。
- 左子树和右子树有各是一颗二叉排序树。
左子树结点值<根节点<右子树结点值
进行中序遍历,可以得到一个递增的有序序列
因此二叉排序树可用于元素的有序组织、搜索
二叉排序树的查找
左子树结点值<根节点值<右子树结点值
若树非空,目标值与根节点的值比较:
- 若相等,则查找成功。
- 若小于根节点,则在左子树上查找,否则在右子树上查找。
查找成功,返回结点指针;查找失败返回NULL
/**
* 二叉搜索树
* @author five-five
* @created 2022/6/2
*
**/
#include "stdio.h"
#include "stdlib.h"
#include "stdbool.h"
typedef struct BSTNode {
int key;
struct BSTNode *lChild;
struct BSTNode *rChild;
} BSTNode, *BSTree;
/**
* 非递归实现
*/
BSTNode *BST_Search(BSTree T, int key) {
while (T != NULL && key != T->key) {//若树空或等于根节点值,则结束循环
if (key < T->key) { //小于,则在左子树查找
T = T->lChild;
} else {//大于,则在右子树查找
T = T->rChild;
}
} return T;
}
/**
* 递归实现
*/
BSTNode *BST_Search(BSTree T, int key) {
if (T == NULL) {
return NULL;
}
if (key==T->key) {
return T;
}
if (key>T->key) {
BST_Search(T->rChild, key);
}
if (key<T->key) {
BST_Search(T->lChild, key);
}
return NULL;
}
二叉排序树的插入
若原二叉排序树为空,则直接插入结点;否则,若关键字 k 小于根节点值,则插入到左子树,若关键字 k 大于根节点值,则插入到右子树。
/**
* 在二叉排序树插入关键字为k的新节点
* @param T * @param k * @return */int BSt_Insert(BSTree *T, int k) {
if (*T == NULL) {
BSTree cur = (BSTree) malloc(sizeof(BSTNode));
cur->key = k;
cur->rChild = NULL;
cur->lChild = cur->rChild;
(*T) = cur;
return 0;
}
BSTree tree = *T;
if (k == tree->key) {//存在相同结点,插入失败
return -1;
}
if (k > tree->key) {//大于,右子树去找
struct BSTNode *rChild = (*T)->rChild;
return BSt_Insert(&rChild, k);
} else {//小于,左子树去找
struct BSTNode *lChild = (*T)->lChild;
return BSt_Insert(&lChild, k);
}
}
二叉排序树的构造
按照数组依次构建二叉排序树 (使用上面 二叉排序树的插入来实现)
注意:不同关键字序列可能得到同款二叉排序树
/**
* 二叉排序树的构建
* @param T 排序树结点,也就是要操作的对象
* @param arr 值数组
* @param length 数组长度
*/
void Create_BST(BSTree *T, int *arr, int length) {
(*T) = NULL;
int i = 0;
while (i < length) {
BST_Insert(T, arr[i]);
i++;
}
}
二叉排序树的删除
- 先 搜索 找到目标结点:
- 若被删除结点 z 是叶子节点,则直接删除,不会破坏二叉排序树的性质
- 若结点 z 只有一颗左子树或右子树,则让 z 的子树成为 z 父节点的子树,替代 z 的位置
- 若结点 z 有左右两颗子树,则令 z 的直接后继 (或直接前驱) 替代 z,然后从二叉排序树中删去这个直接后继 (或直接前驱),这样就转换成了第一或第二种情况。
- 使用直接后继来替换 z 结点
- z 的后继:z 的右子树最左下结点 (该节点一定没有左子树)
- 使用直接前驱来替换 z 结点
- z 的前驱:z 的左子树最右下结点 (该节点一定没有右子树)
- 使用直接后继来替换 z 结点
总结:
二叉排序树的中序遍历是有序的,且为升序。