二叉查找树
二叉查找树 其 基本的执行操作的时间与树的高度成正比。对于n
个节点的 完全二叉查找树 操作的最坏情况为 lgn但对n
个节点的线性链来说,操作的最坏情况为 n。
总之,一个随机构造的二叉查找树的期望高度为 lgn, 基本动态集合操作的平均时间为 lgn
二叉查找树性质
某节点的左子树节点值均小于该节点值,右子树节点值均大于该节点值。
二叉搜索树 按 树的中序遍历 可以得到 从小到大排序的队列
插入节点时,新写了一种使用递归的方法来进行插入,这种方法比较容易理解,不用分多种情况讨论
C代码实现如下
typedef struct TreeNode {
TreeNode *left;
TreeNode *right;
TreeNode *parent;
int data;
} TreeNode;
TreeNode* SEARCH_Recursion(TreeNode *t, int k) { // t为根节点
if (t == NULL || k == t->data) {
return t;
}
if (k < t->data) {
return SEARCH_Recursion(t->left, k);
}
else
return SEARCH_Recursion(t->right, k);
}
TreeNode* SEARCH_Nonrecursion(TreeNode *t, int k) { // t为根节点
while (t != NULL && k != t->data) {
if (k < t->data)
t = t-> left;
else
t = t->right;
}
return t;
}
TreeNode* MINIMUM(TreeNode *t) {
while (t->left != NULL) {
t = t->left;
}
return t;
}
TreeNode* MAXIMUM(TreeNode *t) {
while (t->right != NULL) {
t = t->right;
}
return t;
}
/*
15
/ \
6 18
/ \
17 20
/
16
找16的直接前驱
1、找到17,判断16是17的左孩子
2、找到18,判断17是18的左孩子
3、找到15,判断18是15的右孩子, return 15
# 完
*/
TreeNode* PRESUCCESSOR(TreeNode *t){ // 寻找该节点的按中序遍历的前驱节点
if (t->left != NULL) {
return MAXIMUM(t->left);
}
TreeNode *y = t->parent;
while (y != NULL && t == y->left)// 当左节点为空时,一直向上找,直到找到某个节点是其父节点的左儿子时终止
t = y;
y = y->parent;
}
return y;
}
/*
15
/ \
6 18
/ \
3 7
\
13
找13的直接后继
1、找到7,判断13是7的右孩子
2、找到6,判断7是6的右孩子
3、找到15,判断6是15的左孩子
# 完
*/
TreeNode* SUCCESSOR(TreeNode *t) {
if (t->right != NULL)
return MINIMUM(t->right);
TreeNode *y = t->parent;
while (y != NULL && t == y->right) {// 当左节点为空时,一直向上找,直到找到某个节点是其父节点的左儿子时终止
t = y;
y = y->parent;
}
return y;
}
// 这是非递归的插入
// 二叉查找树插入的位置一定是在叶子节点上,而不会插入到树中间
// 所以只需要改变其指向就好
void INSERT(TreeNode *t, int x) { // 其插入的位置一定在叶子节点上,而不会插入到树中间
TreeNode *node = new TreeNode;
node->data = x;
node->left = node->right = node->parent = NULL;
TreeNode *r = t;
TreeNode *y;
while (r != NULL) { // 寻找x的插入位置
y = r; // 记录位置
if (x < r->data) {
r = r->left;
}
else {
r = r->right;
}
}
node->parent = y;
if (y == NULL) {
t = node; // 若树为空,则node指向根节点
}
else if (x < y->data) {
y->left = node;
}
else
y->right = node;
}
// 此外,还可以利用递归的方法来进行插入。这种方法容易理解,但是在我的代码中,二叉查找树节点包括父
// 亲节点,此时需要给插入节点的父亲节点赋值,这点比较麻烦,因此创建了一个新的函数用来插入
TreeNode* insert_recursion(TreeNode *t, TreeNode *node, TreeNode *p) {
node->parent = p;
if (t == NULL) {
return node;
}
if (t->data >= node->data) {
t->left = insert_recursion(t->left, node, t);
}
else t->right = insert_recursion(t->right, node, t);
return t;
}
TreeNode* insert_recursion(TreeNode *t, int x) {
TreeNode *node = new TreeNode;
node->data = x;
node->left = node->right = NULL;
node->parent = NULL;
if (t == NULL) {
return node;
}
node->parent = t->parent;
return insert_recursion(t, node, t->parent);
}
/*
分三种情况
1、 该节点没有双子女,则直接删除该节点
2、 只有一个子女,直接删除,并将该子女直接用来代替该节点
3、 该节点有双子女,则需要找到它的直接后继,先删除它的直接后继,然后将直接后继的内容代替要删除节点的内容,
*/
void DELETE(TreeNode *t, int x) {
TreeNode *root = t;
TreeNode *node = SEARCH_Nonrecursion(root, x);
TreeNode *y; // y 表示要删除节点
TreeNode *z = NULL; // z 表示要删除节点的孩子
// 确定要删除的节点 y
if (node->left == NULL || node->right == NULL) { // 最多有一个子女
y = node;
}
else { // 有双子女
y = SUCCESSOR(node);
}
if (y->left != NULL)
z = y->left;
else
z = y->right;
if (z != NULL)
z->parent = y->parent; // 将要删除节点y的子女指向y的父亲
if (y->parent == NULL) {
t = z;
}
else if (y == y->parent->left)
y->parent->left = z;
else
y->parent->right = z;
if (y->data != x) { // 若删除的是其后继,则将后继的内容代替node
node->data = y->data;
}
}