查找---动态查找

二叉排序树
二叉排序树定义二叉排序树(Binary Sort Tree)又称二叉查找树,是一种特殊的二叉树。
二叉排序树或者是一棵空树;或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树中所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树中所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
根据二叉排序树的定义,对二叉树进行LDR遍历得到是递增序列。
这里写图片描述
对这棵树中序遍历为:3-12-24-37-45-53-61-78-90-100

插入算法
若二叉树为空。则首先单独生成根结点。
若二叉树非空,则将插入值与二叉排序树根结点的关键字进行比较:
(1)如果key的值等于根结点的值,则停止插入;
(2)如果key的值小于根结点的值,则将结点插入左子树;
(3)如果key的值大于根结点的值,则将结点插入右孩子。
注意:新插入的结点总是叶子结点。

例:将序列:122、99、250、110、300、280 作为二叉排序树的结点的关键字值,生成二叉排序树。
这里写图片描述

算法描述:

//二叉排序树的插入
bool InsertBTNode(BTreeNode **BTNode, int value)
{
    bool bRes = false;

    if (SearchBTNode((*BTNode), value) == NULL)
    {
        BTreeNode *BTreeNewNode = (BTreeNode *)malloc(sizeof(BTreeNode));//新结点

        BTreeNewNode->value = value;
        BTreeNewNode->lChild = NULL;
        BTreeNewNode->rChild = NULL;

        if ((*BTNode) == NULL)
        {
            (*BTNode) = BTreeNewNode;
        }
        else if(value > (*BTNode)->value)
        {
            InsertBTNode(&(*BTNode)->rChild, value);//递归右子树插入
        }
        else if(value < (*BTNode)->value)
        {
            InsertBTNode(&(*BTNode)->lChild, value);//递归左子树插入
        }

        bRes = true;
    }
    else
    {
        bRes = false;
    }

    return bRes;
}

查找算法
因为二叉排序树可看做是一个有序表,所以在二叉排序树上进行查找,和折半查找类似,也是一个逐步缩小查找范围的过程。
算法思想:
首先将待查关键字key与根结点关键字t进行比较,如果:
a.key = t:则返回根结点地址;
b.key < t:则进一步查左子树;
c.key > t:则进一步查右子树。

算法描述:

//二叉排序树的查找
BTreeNode *SearchBTNode(BTreeNode *BTNode, int key)
{
    if (NULL == BTNode)
    {
        return NULL;
    }

    if (BTNode->value == key)//找到,返回
    {
        return BTNode;
    }
    else if(key > BTNode->value)
    {
        return SearchBTNode(BTNode->rChild, key);
    }
    else
    {
        return SearchBTNode(BTNode->lChild, key);
    }

    return NULL;
}

删除结点算法
在二叉排序树中删除结点的原则是:删除结点后仍是二叉排序树。
设在二叉排序树被删除结点是p,其双亲结点为f。(假设结点p是结点f的左孩子)
情况讨论:
(1) 若p为叶结点,则可直接将其删除:f->lchild = NULL;free(p);
(2) 若p结点只有左子树或只有右子树,则可将p的左子树或右子树直接改为其双亲结点f的左子树:f->lchild = p->lchild;free(p);
(3) 若p既有左子树又有右子树,此时有两种处理方法:
方法1:首先找到p结点在中序序列中的直接前驱结点s,然后将p的左子树改为f的左子树,而将p的右子树改为s的右子树:
f->lchild = p->lchild;
s->rchild = p->rchild;
free(p);
方法2:首先找到p结点在中序序列中的直接前驱结点s,然后用s结点的值替代p结点的值,再将s结点删除,原s结点的左子树改为s的双亲结点q的右子树:
p->data = s->data;
q->rchild = s->lchild;
free(s);

算法描述:

void DeleteNode(BTreeNode **BTNode)
{
    BTreeNode *q = NULL;
    BTreeNode *s = NULL;

    if (NULL == (*BTNode)->rChild)//右子树为空,则链接它的左子树
    {
        q = *BTNode;
        (*BTNode) = (*BTNode)->lChild;
        free(q);
    }
    else if(NULL == (*BTNode)->lChild)//左子树为空,则链接它的右子树
    {
        q = (*BTNode);
        (*BTNode) = (*BTNode)->rChild;
        free(q);
    }
    else//左右子树均不为空
    {
        q = (*BTNode);
        s = (*BTNode)->lChild;

        while (s->rChild != NULL)//转到左,然后向右到尽头
        {
            q = s;
            s = s->rChild;
        }

        (*BTNode)->value = s->value;//s指向被删结点的直接前驱

        if (q != (*BTNode))
        {
            q->rChild = s->lChild;
        }
        else
        {
            q->lChild = s->lChild;
        }

        free(s);
    }
}

代码实现:

#ifndef BTreeSort_h__
#define BTreeSort_h__

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

typedef struct _NODE
{
    int value;
    struct _NODE *lChild;
    struct _NODE *rChild;
}BTreeNode;

//二叉排序树的查找
BTreeNode *SearchBTNode(BTreeNode *BTNode, int key);

//二叉排序树的插入
bool InsertBTNode(BTreeNode **BTNode, int value);

//二叉排序树的创建
void CreateBTNode(BTreeNode **BTNode, int *arr, int length);

//遍历输出值
void InOrderTree(BTreeNode *BTNode);

//删除结点值为key的结点
void Delete(BTreeNode **BTNode, int key);

//删除结点值为key的结点
void DeleteBTree(BTreeNode **BTNode, int key);

//删除结点
void DeleteNode(BTreeNode **BTNode);

#endif // BTreeSort_h__

以上代码的实现:

#include "BTreeSort.h"

//二叉排序树的查找
BTreeNode *SearchBTNode(BTreeNode *BTNode, int key)
{
    if (NULL == BTNode)
    {
        return NULL;
    }

    if (BTNode->value == key)
    {
        return BTNode;
    }
    else if(key > BTNode->value)
    {
        return SearchBTNode(BTNode->rChild, key);
    }
    else
    {
        return SearchBTNode(BTNode->lChild, key);
    }

    return NULL;
}

//二叉排序树的插入
bool InsertBTNode(BTreeNode **BTNode, int value)
{
    bool bRes = false;

    if (SearchBTNode((*BTNode), value) == NULL)
    {
        BTreeNode *BTreeNewNode = (BTreeNode *)malloc(sizeof(BTreeNode));

        BTreeNewNode->value = value;
        BTreeNewNode->lChild = NULL;
        BTreeNewNode->rChild = NULL;

        if ((*BTNode) == NULL)
        {
            (*BTNode) = BTreeNewNode;
        }
        else if(value > (*BTNode)->value)
        {
            InsertBTNode(&(*BTNode)->rChild, value);
        }
        else if(value < (*BTNode)->value)
        {
            InsertBTNode(&(*BTNode)->lChild, value);
        }

        bRes = true;
    }
    else
    {
        bRes = false;
    }

    return bRes;
}

//二叉排序树的创建
void CreateBTNode(BTreeNode **BTNode, int *arr, int length)
{
    for (int i = 0; i < length; i++)
    {
        InsertBTNode(&(*BTNode), arr[i]);
    }
}

//遍历输出值
void InOrderTree(BTreeNode *BTNode)
{
    if (NULL != BTNode)
    {
        InOrderTree(BTNode->lChild);
        printf("%d,", BTNode->value);
        InOrderTree(BTNode->rChild);
    }
}

/*
二叉排序树中删除一个结点,不是把以该结点为根的都删掉
假设f结点为p结点的双亲结点
叶子结点--->f->lchild = NULL; free(p);
仅有左或者右子树的结点---->f->lchild = p->lchild;
左右子树都有的结点(比较复杂的情况)两中处理方法:
(1)首先找到p结点在中序序列中的直接前驱s结点,然后将p的左子树改为f的左子树,而将p的右子树改为s的右子树
    f->lchild = p->lchild;s->rchild=p->rchild; free(p);
(2)首先找到p结点所在中序序列中的直接前驱s结点,然后用s结点的值替代p结点的值,再将s结点删除,原来
    s结点的左子树改为s的双亲结点q的右子树
    p->data=s->data;q->rchild=s->lchild;free(s);
*/

//删除结点值为key的结点
void DeleteBTree(BTreeNode **BTNode, int key)
{
    if(NULL == (*BTNode))
    {
        return;
    }
    else
    {
        if (key == (*BTNode)->value)
        {
            DeleteNode(BTNode);
        }
        else if(key > (*BTNode)->value)
        {
            DeleteBTree(&(*BTNode)->rChild, key);
        }
        else
        {
            DeleteBTree(&(*BTNode)->lChild, key);
        }
    }
}

//删除结点
void DeleteNode(BTreeNode **BTNode)
{
    BTreeNode *q = NULL;
    BTreeNode *s = NULL;

    if (NULL == (*BTNode)->rChild)//右子树为空,则链接它的左子树
    {
        q = *BTNode;
        (*BTNode) = (*BTNode)->lChild;
        free(q);
    }
    else if(NULL == (*BTNode)->lChild)//左子树为空,则链接它的右子树
    {
        q = (*BTNode);
        (*BTNode) = (*BTNode)->rChild;
        free(q);
    }
    else//左右子树均不为空
    {
        q = (*BTNode);
        s = (*BTNode)->lChild;

        while (s->rChild != NULL)//转到左,然后向右到尽头
        {
            q = s;
            s = s->rChild;
        }

        (*BTNode)->value = s->value;//s指向被删结点的直接前驱

        if (q != (*BTNode))
        {
            q->rChild = s->lChild;
        }
        else
        {
            q->lChild = s->lChild;
        }

        free(s);
    }
}

测试:

#include "BTreeSort.h"

int main(void)
{
    BTreeNode *Root = NULL;
    int arrint[] = {62,88,58,47,35,73,51,99,37,93};
    int len = sizeof(arrint)/sizeof(arrint[1]);

    CreateBTNode(&Root, arrint, len);

    InOrderTree(Root);
    printf("\b \n");

    DeleteBTree(&Root, 47);
    InOrderTree(Root);
    printf("\b \n");

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值