二叉搜索树

二叉搜索树概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树
  •若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  •若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  •它的左右子树也分别为二叉搜索树
如图所示:
二叉

二叉搜索树操作

1.插入
(1)如果这个树是空树,直接创建一个节点
(2)如果这个树是非空树
  a)先查找到一个合适的插入位置
  b)再在对应的位置上创建节点

插入

2.查找
(1)如果这个树是空树,查找失败
(2)如果这个树是非空树
  a)如果根节点key == 查找key,查找成功
  b)如果根节点key > 查找key,在左子树查找
  c)如果根节点key < 查找key,在右子树查找

查找

3.删除
(1)如果这个树是空树,直接返回
(2)如果这个树是非空树,找到要删除节点的位置,以及要删除节点的父节点
(3)如果要删除元素在树中没有找到,也直接返回
(4)如果要删除节点找到了,分情况讨论:
  a)要删除节点没有子树,直接删除该节点,并将该节点的父节点置空
  b)要删除节点,只有左子树,删除该节点的同时,将该节点的左子树挂到父节点上
  c)要删除节点,只有右子树,删除该节点的同时,将该节点的右子树挂到父节点上
  d)要删除节点,同时有左右子树,找到右子树中的最小值,然后将该最小值赋值给要删除的位置,然后再删除该右子树中的最小值节点,这样的节点最多只有一个右子树,就转换成了情况c)

删除

具体实现代码如下:

①search_tree.h

//实现二叉搜索树的递归和非递归版本

#pragma once 

typedef char SearchTreeType;

typedef struct SearchTreeNode {
    SearchTreeType key; // 关键码 
    struct SearchTreeNode* lchild;
    struct SearchTreeNode* rchild;
} SearchTreeNode;

void SearchTreeInit(SearchTreeNode** root);               //初始化

void SearchTreeInsert1(SearchTreeNode** root, SearchTreeType key);          //插入

void SearchTreeInsert2(SearchTreeNode** root, SearchTreeType key);          //插入(递归)

SearchTreeNode* SearchTreeFind1(SearchTreeNode* root,SearchTreeType to_find);          //查找

SearchTreeNode* SearchTreeFind2(SearchTreeNode* root, SearchTreeType to_find);          //查找(递归)

void SearchTreeRemove1(SearchTreeNode** root, SearchTreeType key);             //删除

void SearchTreeRemove2(SearchTreeNode** root, SearchTreeType key);             //删除(递归)

②search_tree.c

#include "search_tree.h"

#include <stdlib.h>

void SearchTreeInit(SearchTreeNode** root)              //初始化
{
    if (root == NULL)
    {
        return;          //非法输入
    }
    *root = NULL;
    return;
}

SearchTreeNode* CreateSearchTreeNode(SearchTreeType key)
{
    SearchTreeNode* new_node = (SearchTreeNode*)malloc(sizeof(SearchTreeNode));
    new_node->key = key;
    new_node->lchild = NULL;
    new_node->rchild = NULL;
    return new_node;
}

void SearchTreeInsert1(SearchTreeNode** root, SearchTreeType key)          //插入
{
    if (root == NULL)
    {
        return;        //非法输入
    }
    //1.如果这个树是空树,直接创建一个节点
    if (*root == NULL)
    {
        *root = CreateSearchTreeNode(key);
        return;
    }
    //2.如果这个树是非空树
    SearchTreeNode* cur = *root;
    SearchTreeNode* parent = NULL;
    while (1)
    {
        //  a)先查找到一个合适的插入位置
        if (cur == NULL)
        {
            break;        //合适的位置已找到
        }
        if (key < cur->key)
        {
            parent = cur;
            cur = cur->lchild;
        }
        else if (key > cur->key)
        {
            parent = cur;
            cur = cur->rchild;
        }
        else
        {
            return;          //约定当插入的值与当前元素相等时,插入失败
        }
    }
    //  b)再在对应的位置上创建节点
    SearchTreeNode* new_node = CreateSearchTreeNode(key);
    if (key < parent->key)
    {
        parent->lchild = new_node;
    }
    else
    {
        parent->rchild = new_node;
    }
    return;
}

void SearchTreeInsert2(SearchTreeNode** root, SearchTreeType key)          //插入(递归)
{
    if (root == NULL)
    {
        return;          //非法输入
    }
    //1.先找到在哪个位置插入
    //2.创建新的节点,放在对应位置上
    if (*root == NULL)
    {
        *root = CreateSearchTreeNode(key);
        return;
    }
    if (key < (*root)->key)
    {
        SearchTreeInsert2(&(*root)->lchild, key);
        return;
    }
    else if (key > (*root)->key)
    {
        SearchTreeInsert2(&(*root)->rchild, key);
        return;
    }
    else
    {
        return;      //插入失败
    }
}

SearchTreeNode* SearchTreeFind1(SearchTreeNode* root, SearchTreeType to_find)          //查找
{
    if (root == NULL)
    {
        return;       //非法输入
    }
    SearchTreeNode* cur = root;
    while (cur != NULL)
    {
        if (to_find < cur->key)
        {
            cur = cur->lchild;
        }
        else if (to_find > cur->key)
        {
            cur = cur->rchild;
        }
        else
        {
            break;
        }
    }
    return cur;
}

SearchTreeNode* SearchTreeFind2(SearchTreeNode* root, SearchTreeType to_find)          //查找(递归)
{
    if (root == NULL)
    {
        return NULL;        //非法输入
    }
    if (to_find < root->key)
    {
        SearchTreeFind2(root->lchild, to_find);
    }
    else if (to_find > root->key)
    {
        SearchTreeFind2(root->rchild, to_find);
    }
    else
    {
        return root;
    }
}

void DestroySearchTreeNode(SearchTreeNode* to_remove)
{
    free(to_remove);
}
void SearchTreeRemove1(SearchTreeNode** root, SearchTreeType key)             //删除
{
    if (root == NULL)
    {
        return;        //非法输入
    }
    //1.处理空树情况,直接返回
    if (*root == NULL)
    {
        return;
    }
    SearchTreeNode* to_remove = *root;
    SearchTreeNode* parent = NULL;
    //2.找到要删除节点的位置,以及要删除节点的父节点
    while (to_remove != NULL)
    {
        if (key < to_remove->key)
        {
            parent = to_remove;
            to_remove = to_remove->lchild;
        }
        else if (key > to_remove->key)
        {
            parent = to_remove;
            to_remove = to_remove->rchild;
        }
        else
        {
            break;
        }
    }
    //3.如果要删除元素在树中没有找到,也直接返回
    if (to_remove == NULL)
    {
        return;
    }
    //4.如果要删除节点找到了,分情况讨论:
    //     a)要删除节点没有子树,直接删除该节点,并将该节点的父节点置空
    if (to_remove->lchild == NULL && to_remove->rchild == NULL)
    {
        if (to_remove == *root)
        {
            //要删除的是根节点
            *root = NULL;
        }
        else
        {
            if (parent->lchild == to_remove)
            {
                parent->lchild = NULL;
            }
            else
            {
                parent->rchild = NULL;
            }
            DestroySearchTreeNode(to_remove);
            return;
        }
    }
    //     b)要删除节点,只有左子树,删除该节点的同时,将该节点的左子树挂到父节点上
    else if(to_remove->lchild != NULL && to_remove->rchild == NULL)
    {
        if (to_remove == *root)
        {
            //要删除的是根节点
            *root = to_remove->lchild;
            DestroySearchTreeNode(to_remove);
        }
        else
        {
            if (to_remove == parent->lchild)
            {
                parent->lchild = to_remove->lchild;
            }
            else
            {
                parent->rchild = to_remove->lchild;
            }
            DestroySearchTreeNode(to_remove);
            return;
        }
    }
    //     c)要删除节点,只有右子树,删除该节点的同时,将该节点的右子树挂到父节点上
    else if (to_remove->lchild == NULL && to_remove->rchild != NULL)
    {
        if (to_remove == *root)
        {
            //要删除的是根节点
            *root = to_remove->rchild;
            DestroySearchTreeNode(to_remove);
        }
        else
        {
            if (to_remove == parent->lchild)
            {
                parent->lchild = to_remove->rchild;
            }
            else
            {
                parent->rchild = to_remove->rchild;
            }
            DestroySearchTreeNode(to_remove);
            return;
        }
    }
    //     d)要删除节点,同时有左右子树,找到右子树中的最小值,
    //       然后将该最小值赋值给要删除的位置,然后再删除该右子树中的最小值节点,
    //       这样的节点最多只有一个右子树,就转换成了情况c)
    else
    {
        SearchTreeNode* min = to_remove->rchild;
        SearchTreeNode* min_parent = to_remove;
        while (min->lchild != NULL)
        {
            min_parent = min;
            min = min->lchild;
        }
        //代码执行到这里,min就已经指向to_remove右子树的最小值
        to_remove->key = min->key;
        if (min_parent->lchild == min)
        {
            min_parent->lchild = min->rchild;
        }
        else
        {
            min_parent->rchild = min->rchild;
        }
        DestroySearchTreeNode(min);
        return;
    }
}

void SearchTreeRemove2(SearchTreeNode** root, SearchTreeType key)             //删除(递归)
{
    if (root == NULL)
    {
        return;     //非法输入
    }
    //1.处理空树情况,直接返回
    if (*root == NULL)
    {
        return;
    }
    //2.如果没找到的话,不需要进行任何删除动作
    if (key < (*root)->key)
    {
        SearchTreeRemove2(&(*root)->lchild, key);
        return;
    }
    else if (key>(*root)->key)
    {
        SearchTreeRemove2(&(*root)->rchild, key);
        return;
    }
    //3.找到后分情况讨论:
    else
    {
        SearchTreeNode* to_remove = *root;
        //     a)要删除节点没有子树,直接删除该节点,并将该节点的父节点置空
        if (to_remove->lchild == NULL && to_remove->rchild == NULL)
        {
            *root = NULL;
            DestroySearchTreeNode(to_remove);
            return;
        }
        //     b)要删除节点,只有左子树,删除该节点的同时,将该节点的左子树挂到父节点上
        else if (to_remove->lchild != NULL && to_remove->rchild == NULL)
        {
            *root = to_remove->lchild;
            DestroySearchTreeNode(to_remove);
            return;
        }
        //     c)要删除节点,只有右子树,删除该节点的同时,将该节点的右子树挂到父节点上
        else if (to_remove->lchild == NULL && to_remove->rchild != NULL)
        {
            *root = to_remove->rchild;
            DestroySearchTreeNode(to_remove);
            return;
        }
        //     d)要删除节点,同时有左右子树,找到右子树中的最小值,
        //       然后将该最小值赋值给要删除的位置,然后再删除该右子树中的最小值节点,
        //       这样的节点最多只有一个右子树,就转换成了情况c)
        else
        {
            SearchTreeNode* min = to_remove->rchild;
            while (min->lchild != NULL)
            {
                min = min->lchild;
            }
            //代码执行到这里,min就已经指向to_remove右子树的最小值
            to_remove->key = min->key;
            SearchTreeRemove2(&to_remove->rchild, min->key);
            return;
        }
    }
}

③test.c

#include "search_tree.h"

#include <stdio.h>
#include <windows.h>

#define TEST_HEADER printf("\n===========================%s====================\n",__FUNCTION__)

void TestInit()
{
    TEST_HEADER;
    SearchTreeNode* root;
    SearchTreeInit(&root);
    printf("root except NULL,actual %p\n", root);
}

void PreOrder(SearchTreeNode* root)
{
    if (root == NULL)
    {
        return;
    }
    printf("%c ", root->key);
    PreOrder(root->lchild);
    PreOrder(root->rchild);
    return;
}

void InOrder(SearchTreeNode* root)
{
    if (root == NULL)
    {
        return;
    }
    InOrder(root->lchild);
    printf("%c ", root->key);
    InOrder(root->rchild);
    return;
}

void TestInsert1()
{
    TEST_HEADER;
    SearchTreeNode* root;
    SearchTreeInit(&root);
    SearchTreeInsert1(&root, 'd');
    SearchTreeInsert1(&root, 'h');
    SearchTreeInsert1(&root, 'f');
    SearchTreeInsert1(&root, 'a');
    SearchTreeInsert1(&root, 'l');
    SearchTreeInsert1(&root, 'e');
    SearchTreeInsert1(&root, 'c');
    printf("[先序遍历结果]:");
    PreOrder(root);
    printf("\n");
    printf("[中序遍历结果]:");
    InOrder(root);
    printf("\n");
}

void TestInsert2()
{
    TEST_HEADER;
    SearchTreeNode* root;
    SearchTreeInit(&root);
    SearchTreeInsert2(&root, 'd');
    SearchTreeInsert2(&root, 'h');
    SearchTreeInsert2(&root, 'f');
    SearchTreeInsert2(&root, 'a');
    SearchTreeInsert2(&root, 'l');
    SearchTreeInsert2(&root, 'e');
    SearchTreeInsert2(&root, 'c');
    printf("[先序遍历结果]:");
    PreOrder(root);
    printf("\n");
    printf("[中序遍历结果]:");
    InOrder(root);
    printf("\n");
}

void TestFind1()
{
    TEST_HEADER;
    SearchTreeNode* root;
    SearchTreeInit(&root);
    SearchTreeInsert1(&root, 'd');
    SearchTreeInsert1(&root, 'h');
    SearchTreeInsert1(&root, 'f');
    SearchTreeInsert1(&root, 'a');
    SearchTreeInsert1(&root, 'l');
    SearchTreeInsert1(&root, 'e');
    SearchTreeInsert1(&root, 'c');

    SearchTreeNode* ret = SearchTreeFind1(root, 'f');
    printf("查找f:ret expect %p,actual %p\n", root->rchild->lchild, ret);

    ret = SearchTreeFind1(root, 'c');
    printf("查找c:ret expect %p,actual %p\n", root->lchild->rchild, ret);
}

void TestFind2()
{
    TEST_HEADER;
    SearchTreeNode* root;
    SearchTreeInit(&root);
    SearchTreeInsert1(&root, 'd');
    SearchTreeInsert1(&root, 'h');
    SearchTreeInsert1(&root, 'f');
    SearchTreeInsert1(&root, 'a');
    SearchTreeInsert1(&root, 'l');
    SearchTreeInsert1(&root, 'e');
    SearchTreeInsert1(&root, 'c');

    SearchTreeNode* ret = SearchTreeFind2(root, 'f');
    printf("查找f:ret expect %p,actual %p\n", root->rchild->lchild, ret);

    ret = SearchTreeFind2(root, 'c');
    printf("查找c:ret expect %p,actual %p\n", root->lchild->rchild, ret);
}

void TestRemove1()
{
    TEST_HEADER;
    SearchTreeNode* root;
    SearchTreeInit(&root);
    SearchTreeInsert1(&root, 'd');
    SearchTreeInsert1(&root, 'h');
    SearchTreeInsert1(&root, 'f');
    SearchTreeInsert1(&root, 'a');
    SearchTreeInsert1(&root, 'l');
    SearchTreeInsert1(&root, 'e');
    SearchTreeInsert1(&root, 'c');
    printf("[先序遍历结果]:");
    PreOrder(root);
    printf("\n");
    printf("[中序遍历结果]:");
    InOrder(root);
    printf("\n");

    SearchTreeRemove1(&root, 'h');
    printf("删除h后:\n");
    printf("[先序遍历结果]:");
    PreOrder(root);
    printf("\n");
    printf("[中序遍历结果]:");
    InOrder(root);
    printf("\n");

    SearchTreeRemove1(&root, 'd');
    printf("删除d后:\n");
    printf("[先序遍历结果]:");
    PreOrder(root);
    printf("\n");
    printf("[中序遍历结果]:");
    InOrder(root);
    printf("\n");

    SearchTreeRemove1(&root, 'c');
    printf("删除c后:\n");
    printf("[先序遍历结果]:");
    PreOrder(root);
    printf("\n");
    printf("[中序遍历结果]:");
    InOrder(root);
    printf("\n");
}

void TestRemove2()
{
    TEST_HEADER;
    SearchTreeNode* root;
    SearchTreeInit(&root);
    SearchTreeInsert1(&root, 'd');
    SearchTreeInsert1(&root, 'h');
    SearchTreeInsert1(&root, 'f');
    SearchTreeInsert1(&root, 'a');
    SearchTreeInsert1(&root, 'l');
    SearchTreeInsert1(&root, 'e');
    SearchTreeInsert1(&root, 'c');
    printf("[先序遍历结果]:");
    PreOrder(root);
    printf("\n");
    printf("[中序遍历结果]:");
    InOrder(root);
    printf("\n");

    SearchTreeRemove2(&root, 'h');
    printf("删除h后:\n");
    printf("[先序遍历结果]:");
    PreOrder(root);
    printf("\n");
    printf("[中序遍历结果]:");
    InOrder(root);
    printf("\n");

    SearchTreeRemove2(&root, 'd');
    printf("删除d后:\n");
    printf("[先序遍历结果]:");
    PreOrder(root);
    printf("\n");
    printf("[中序遍历结果]:");
    InOrder(root);
    printf("\n");

    SearchTreeRemove2(&root, 'c');
    printf("删除c后:\n");
    printf("[先序遍历结果]:");
    PreOrder(root);
    printf("\n");
    printf("[中序遍历结果]:");
    InOrder(root);
    printf("\n");
}

int main()
{
    TestInit();
    TestInsert1();
    TestInsert2();
    TestFind1();
    TestFind2();
    TestRemove1();
    TestRemove2();
    system("pause");
    return 0;
}

测试结果如下图所示:

结果
结果

二叉搜索树性能分析

  插入和删除操作都必须先查找,查找效率代表二叉搜索树中各个操作的性能。
  对有n个节点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是节点在二叉搜索树的深度的函数,即节点越深,则比较次数越多
  但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树,如:

性能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值