【数据结构学习笔记】二叉排序树

二叉排序树

性质

  1. 左子树 < \lt <根节点

  2. 右子树 > \gt >根节点

  3. 中序遍历结果,是一个有序序列

数据结构,就是定义一种形值,并维护这种性质的数据结构

插入操作

  1. 插入新节点,一定会作为叶子节点

删除操作

  1. 删除度为0的节点,直接删除

  2. 删除度为1的节点,把其子树挂到其父节点上面

  3. 删除度为2的节点,可以转换成为删除度为1或者度为0的节点

对于度为2的节点:

  1. 前驱:左子树的最大值

  2. 后继:右子树的最小值

随堂练习题

​ 按照如下顺序插入数字,画出对应的二叉搜索树

  1. [ 5 9 8 3 2 4 1 7 ]
  2. [1 2 3 4 5 ]
  3. [3 4 5 1 2 ]
  1. 插入顺序会影响最终的树形结构

  2. 不同的树形结构,查找效率是不同的

平均查找效率:节点查找次数的期望值(假设每个节点等概率被查找) 总 次 数 节 点 数 量 \frac{总次数}{节点数量}

扩展内容

  1. 二叉排序树的删除代码优化

    1. 删除处理度为0的的代码逻辑,不影响代码整体功能

  2. 如何解决排名相关的检索需求

    1. 修改二叉排序树的结构定义,增加一个size字段,记录每棵树的节点数量

    2. L S = k − 1 LS = k-1 LS=k1,根节点就是排名第k位的元素 (LS:左子树节点数量)

    3. k ≤ L S k \le LS kLS,排名第k位的元素在左子树中去找

    4. k ≥ L S k \ge LS kLS s e a r c h k ( r o o t − > r c h i l d , k − L S − 1 ) search_k(root->rchild, k - LS - 1) searchk(root>rchild,kLS1) (在右子树寻找第 K - LS - 1个元素)

  3. 如何解决Top-K问题

    1. 根节点就是第k位元素,就把左子树的值全部输出出来

    2. 第k位元素在左子树,前k位元素全部都在左子树

    3. 第k位元素在右子树,说明根节点和左子树的元素,都是、需要输出

思考:快速排序与二叉排序树在思维逻辑结构上有什么相同点,对照二叉排序树的性质和快速排序算法的partition操作

代码实现

#include <stdio.h>
#include <stdlib.h>

#define KEY(root) (root ? root->key : 0)
#define SIZE(root) (root ? root->size : 0)
#define L(root) (root ? root->lchild : NULL)


typedef struct Node {
    int key;
    int size; // 排名问题
    struct Node *lchild, *rchild;
} Node;

Node *getNewNode(int val) {
    Node *p = (Node *)malloc(sizeof(Node));
    p->key = val;
    p->size = 1; // 排名问题
    p->lchild = p->rchild = NULL;
    return p;
}

int search(Node *root, int val) {
    if (root == NULL) return 0;
    if (root->key == val) return 1;
    if (val < root->key) return search(root->lchild, val);
    return search(root->rchild, val);
}
 
void update_size(Node *root) { // 排名问题
    root->size = SIZE(root->lchild) + SIZE(root->rchild) + 1;
    return ;
}

Node *insert(Node *root, int val) {
    if (root == NULL) return getNewNode(val);
    if (root->key == val) return root;
    if (val < root->key) root->lchild = insert(root->lchild, val);
    else root->rchild = insert(root->rchild, val);
    update_size(root); // 排名问题
    return root;
}

Node *predecessor(Node *root) {
    Node *temp = root->lchild;
    while (temp->rchild) temp = temp->rchild;
    return temp;
}

Node *earse(Node *root, int val) {
    if (root == NULL) return NULL;
    if (val < root->key) {
        root->lchild = earse(root->lchild, val);
    } else if (val > root->key) {
        root->rchild = earse(root->rchild, val);
    } else {
        if (root->lchild == NULL && root->rchild == NULL) {
            free(root);
            return NULL;
        } else if (root->lchild == NULL || root->rchild == NULL) {
            Node *temp = root->lchild ? root->lchild : root->rchild;
            free(root);
            return temp;
        } else {
            Node *temp = predecessor(root);
            root->key = temp->key;
            root->lchild = earse(root->lchild, temp->key);
        }
    }
    update_size(root); // 排名问题
    return root;
}

void output(Node *root) {
    if (root == NULL) return ;
    output(root->lchild);
    printf("%d(%d, %d)\n", KEY(root), KEY(root->lchild), KEY(root->rchild));
    output(root->rchild);
    return ;
}

void pre_output(Node *root) {
    if (root == NULL) return ;
    printf("%d(%d, %d)\n", KEY(root), KEY(root->lchild), KEY(root->rchild));
    pre_output(root->lchild);
    pre_output(root->rchild);
    return ;
}

int search_k(Node *root, int k) { // 排名问题
    if (root == NULL) return -1;
    if (SIZE(L(root)) == k - 1) return root->key;
    if (k <= SIZE(L(root))) {
        return search_k(root->lchild, k);
    }
    return search_k(root->rchild, k - SIZE(L(root)) - 1);
}

void print(Node *root) {
    printf("%d(%d, %d)\n", KEY(root), KEY(root->lchild), KEY(root->rchild));
    return ;
}

void output_k(Node *root, int k) { // top-k 出输前k
    if (k == 0 || root == NULL) return ;
    if (k <= SIZE(L(root))) {
        output_k(root->lchild, k);
    } else {
        output(root->lchild);
        print(root);
        output_k(root->rchild, k - SIZE(L(root)) - 1);
    }
    return ;
}

void clear(Node *root) {
    if (root == NULL) return ;
    clear(root->lchild);
    clear(root->rchild);
    free(root);
    return ;
}

int main() {
    int op, val;
    Node *root = NULL;
    while (~scanf("%d%d", &op, &val)) {
        switch (op) {
            case 0: printf("search %d, result = %d\n", val, search(root, val)); break;
            case 1: root = insert(root, val); break;
            case 2: root = earse(root, val); break;
            case 3:{
                printf("search_k %d, result = %d\n", val, search_k(root, val));
            } break;
            case 4: {
                printf("output_k top-%d elements\n", val);
                output_k(root, val);
                printf("+++++++++++++++++++++\n");
            }
        }
        if (op == 1 || op == 2) {
            output(root);
            printf("-----------------------------\n");
        }
    }
    printf("============================\n");
    pre_output(root);
    printf("============================\n");
    clear(root);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值