常用数据结构----二叉查找树(C实现)

二叉查找树

二叉查找树 其 基本的执行操作的时间与树的高度成正比。对于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;
    }   
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值