《高级数据结构》-二叉排序树

二叉排序树综述

在这里插入图片描述

代码演示

二叉排序树实现

/*************************************************************************
	> File Name: binary_search_tree.cpp
	> Mail: 1136984246@qq.com
 ************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#define SIZE(n) (n ? n->size : 0) // n == NULL, 表示叶子结点的孩子结点,数量为0
#define L(n) (n ? n->lchild : NULL) // n == NULL, 无左子树, n != NULL,返回左子树根节点  
#define KEY(n) (n ? n->key : -1) // -1: 表示NULL

// 底层是二叉树,增加了size字段
typedef struct Node {
    int key;  
    int size; // size 节点所在子树的节点数量
    struct Node *lchild, *rchild; 
} Node;

// 初始化:返回存储key值的结点Node
Node *getNewNode(int key) {
    Node *p = (Node *)malloc(sizeof(Node));
    p->key = key;
    p->size = 1;
    p->lchild = p->rchild = NULL;
    return p;
}

// 更新子树(包括根结点)的结点数 
void update_size(Node *root) {
    root->size = SIZE(root->lchild) + SIZE(root->rchild) + 1;
    return;
}

// 查找:返回int,表示有无
int search(Node *root, int key) {
    if (root == NULL) return 0;
    if (root->key == key) return 1; // 递归考虑边界条件
    if (key < root->key) return search(root->lchild, key);
    return search(root->rchild, key);
}

// 排名:查找排名第k位(从小往大)的元素,返回元素值[假设k合理]
int search_k(Node *root, int k) {
    if (root == NULL) return -1;
    if (SIZE(L(root)) == k - 1)  return root->key; // 左子树的所有key值都比根节点key值小 
                // SIZE(L())有点像函数式编程
    // 在左子树中
    if (k <= SIZE(L(root))) {
        return search_k(root->lchild, k);
    }
    // 在右子树中
    return search_k(root->rchild, k - SIZE(L(root)) - 1); // 更新要找的k值
}

// ? 什么时候插入会改变根节点的地址
// 插入:根节点的地址可能会变化, 返回地址
Node *insert(Node *root, int key) {
    if (root == NULL) return getNewNode(key); // 成功插入
    if (root->key == key) return root; // 不可重复插入
    if (key < root->key) root->lchild = insert(root->lchild, key); // ? 同样可以修改根节点
    else root->rchild = insert(root->rchild, key); 
    update_size(root); // 更新节点数 
    return root;
}

// ? bug : 只有度为2的有前驱, 度为1的也有前驱(不在子树上)
// ? 用后驱可以做吗?
// 前驱(左子树中最大的节点):只是查找[度为2]的节点前驱
Node *predecessor(Node *root) {
    Node *temp = root->lchild; // 左子树
    while (temp->rchild) temp = temp->rchild; // 不断找右节点
    return temp;
}

// 删除为key值得节点:可能会修改根节点
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) {
            // 度为0(取&&)和度为1时,都可以这么操作
            // 找到唯一子结点 [度为0是为NULL]
            Node *temp = root->lchild ? root->lchild : root->rchild;
            free(root); // 防止内存泄漏
            return temp; // 这里要返回temp,其他情况返回root,因为要free root
        } else {
            // 度为2时[找前驱,覆盖根节点的值,删前驱->度最多为1]
            Node *temp = predecessor(root);
            root->key = temp->key;
            root->lchild = erase(root->lchild, temp->key); 
        }
    }
    update_size(root); // 只修改root的,temp的不变
    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;
}

// Top-k
void output_k(Node *root, int k) {
    if (k == 0 || root == NULL) return ;
    if (k <= SIZE(L(root))) {
        // 直接输出左子树的前k位元素
        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;
    Node *root = NULL; // 记得先定义根节点
    while (~scanf("%d%d", &op, &val)) {
        switch(op) {
            case 0 : printf("search %d, result : %d\n----------\n", val, search(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));
                printf("-------------\n");
            } break;
            case 4 : 
                printf("output top-%d element.\n", val);
                output_k(root, val);
                printf("-------------\n");
                break;
        }
        // 插入或删除后输出树形结构
        if (op == 1 || op == 2) {
            if (op == 1) printf("insert %d\n", val);
            else printf("delete %d\n", val);
            output(root);
            printf("-------------\n");
        }
    }
    return 0;
}

随机数测试文件

/*************************************************************************
	> File Name: rand.cpp
	> Mail: 1136984246@qq.com
 ************************************************************************/

#include<iostream>
using namespace std;

int main() {
    srand(time(0));
    int op, val;
    #define MAX_OP 20
    for (int i = 0; i <MAX_OP; i++) {
        switch (rand() % 7) {
            // 非等概率操作 1:3:1 = 查:插:删
            case 0 : printf("0 %d\n", rand() % 15); break;
            case 1 :
            case 2 :
            case 3 : printf("1 %d\n", rand() % 15); break;
            case 4 : printf("2 %d\n", rand() % 15); break;
            case 5 : printf("3 %d\n", rand() % 5); break; 
            case 6 : printf("4 %d\n", rand() % 5); break;
        }
    }
    return 0;
}

测试结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考文献

二叉排序树、AVL树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值