二叉排序树

二叉排序树

又称二叉检索树、二叉搜搜索树

一、性质

​ 1、左节点 < 根结点

​ 2、右节点 > 根结点

​ 3、中序遍历的结果,是一个有序序列

二、用途

解决与排名相关的检索需求

点与边的关系:点是集合,边是关系,根结点相当于全集

三、插入

与根结点相比较,小于根结点,递归左边,大于根结点,递归右边,若是递归到叶子结点,变成叶子结点的左孩子或右孩子。

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

四、删除

三种情况:

一、度为0:

​ 直接删除,返回空节点

二、度为1:

​ 删除节点后,将子孩子交给父亲结点

三、度为2:

​ 找到前驱后或后继结点进行替换,问题转换为删除度为一的问题。

注意:度为2结点的前驱或者后继的一定为度为1的结点

对于度为二的节点:

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

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

练习-1

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

1 :[5, 9, 8, 3, 2, 4, 1, 7]

2 : [1, 2, 3, 4, 5]

在这里插入图片图描述

在这里插入图片描述

1.插入顺序会影响最终的树形结构

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

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

五、代码实现

#include <stdio.h>
#include <stdlib.h>
#define KEY(n) (n ? n->key : 0)

typedef struct Node {
    int key;
    struct Node *lchild, *rchild;
} Node;

Node *getNewNode (int val) {
    Node *p = (Node *)malloc(sizeof(Node));
    p->key = val;
    p->lchild = p->rchild = NULL;
    return p;
}

int search_Node(Node *root, int val) {
    if (root == NULL) return 0;
    if (root->key == val) return 1;
    if (root->key < val) return search_Node(root->rchild, val);
    return search_Node(root->lchild, val);
}

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

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

Node *erase(Node * root, int key) {
    if (root == NULL) return NULL;
    if (key < root->key) {
        root->lchild = erase(root->lchild, key);
    } else if (key > root->key) {
        root->rchild = erase(root->rchild, key);
    } 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 = erase(root->lchild, root->key);
        }
    }
    return root;
}

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

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 ;
}
int main() {
    int op, val; // 操作:0:查找 1:插入 2:删除
    Node *root = NULL;
    while (~scanf("%d%d", &op, &val)) {
        switch (op) {
            case 0: printf("search %d, result : %d\n", val, search_Node(root, val)); break;
            case 1: root = insert(root, val); break;
            case 2: root = erase(root, val); break;
        }
        if (op) {
            output(root);
            printf("-------------\n");
        }
    }
    return 0;
}

六、拓展内容

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

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

2.如何解决排名相关的代码需求

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

​ 2. k = L − 1 k = L - 1 k=L1,根结点就是排名第K位的元素

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

​ 4. k > L S , s e a r c h _ k ( r o o t − > r c h i l d , k − L S − 1 ) k \gt LS, search\_k(root->rchild, k - LS - 1) k>LSsearch_k(root>rchild,kLS1)

3.解决Top-k问题(找到小于第k位的所有元素)

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

​ 2.第K位在左子树中,前k位元素全都在左子树中

​ 3.第k位在右子树中,说明根结点和左子树中的元素,都是前k位元素里的值

4.二叉排序树与快速排序之间的关系

​ 1. 二叉排序树是快速排序在逻辑结构层面用的数据结构

​ 2. 思考1:快速排序的时间复杂度和二叉排序树建树时间复杂度之间的关系

​ 3. 思考2:快速选择算法和二叉排序树之间的关系

​ 4. 程序=算法+数据结构

所谓算法设计及分析能力:分类讨论及归纳总结的能力

七、最终代码

#include <stdio.h>
#include <stdlib.h>
#define KEY(n) (n ? n->key : 0)
#define SIZE(n) (n ? n->size : 0)
#define L(n) (n ? n->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->lchild = p->rchild = NULL;
    p->size = 1;
    return p;
}

void update_size(Node *root) {
    root->size = SIZE(root->lchild) + SIZE(root->rchild) + 1;
    return ;
}

int search_Node(Node *root, int val) {
    if (root == NULL) return 0;
    if (root->key == val) return 1;
    if (root->key < val) return search_Node(root->rchild, val);
    return search_Node(root->lchild, val);
}

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

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);
}

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

Node *erase(Node * root, int key) {
    if (root == NULL) return NULL;
    if (key < root->key) {
        root->lchild = erase(root->lchild, key);
    } else if (key > root->key) {
        root->rchild = erase(root->rchild, key);
    } 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 = erase(root->lchild, root->key);
        }
    update_size(root);
    }
    return root;
}

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

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

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

void output_k(Node *root, int k) {
    if (root == NULL || k == 0) 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 ;
}

int main() {
    int op, val; // 操作:0:查找 1:插入 2:删除 3:查找第k元素 4:打印前Top-k元素
    Node *root = NULL;
    while (~scanf("%d%d", &op, &val)) {
        switch (op) {
            case 0: printf("search %d, result : %d\n", val, search_Node(root, val)); break;
            case 1: root = insert(root, val); break;
            case 2: root = erase(root, val); break;
            case 3: printf("search_k = %d, result : %d\n", val, search_k(root, val)); break;
            case 4: { output_k(root, val); printf("------------\n"); }break;
        }
        if (op == 1 || op == 2) {
            output(root);
            printf("-------------\n");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值