数据结构一一二叉搜索树

什么是树

树在计算机科学里,是一种十分常见的数据结构,几乎所有操作系统都将文件存放在树状的结构里。

在这里插入图片描述

从上面的图来看,树是由节点和边构成,整棵树有一个最上端节点,称为跟节点。每个节点可以拥有具方向性的边用来和其他节点相连。相连的节点中,在上者称为父节点,在下者称为子节点。无子节点称为叶节点。子节点可以存在多个,如果最多只允许两个子节点,即所谓二叉树。根节点的至任何节点之间有唯一的路径,路径所经过的边数为路径长度。

整棵树的高度,便以跟节点的高度来代表,节点A->B之间如果存在一条路径,那么A称为B的祖代,B称为A的子代。任何节点大小是指其所有子代的节点总数。

本文会对树的基本概念做介绍,但重点介绍二叉搜索树。

二叉搜索树

所谓的二叉树,其意义是:“任何节点最多只允许两个子节点”这两个子节点称为左子节点和右子节点,如果以递归的方式来定义二叉树,可以说:“一个二叉树如果不为空,便是由一个根节点和左右两个子树构成;左右子树都可能为空”。

图1-1所示是一棵二叉搜索树:
在这里插入图片描述

二叉搜索树的节点放置规则是:**任何节点的键值大于其左子树中的每一个节点的键值,并小于其右子树中的每一个节点的键值。**从根节点一直往左走,直到无路可走,即得到最小的元素;从根节点一直往右走,直到无路可走,即得到最大元素。

要在一棵二叉搜索树中找到最大元素或最小元素,是一件简单的事情,就像上面说的一样,一直往左走或一直往右走即是。

最大元素:

ElemType FindMax(BTreePtr T) {
    ElemType max;

    while(T != NULL) {
        max = T->data;
        T = T->rchild;
    }
    return max;
}

最小元素:

ElemType FindMin(BTreePtr T) {
    ElemType min;

    while(T != NULL) {
        min = T->data;
        T = T->lchild;
    }
    return min;
}

比较麻烦的是元素的插入和移除。图1-2是二叉搜索树的元素插入操作图解。插入新元素时,可从根节点开始,遇到键值较大的向右子树插入,遇键值较小者就向左子树插入。

插入节点值16,与根节点比较,比根节点小,因此将插入到左子树。

在这里插入图片描述
二叉搜索树的插入:

int Insert(BTreePtr *T, ElemType e) {

    BTreePtr p;
    //创建一个节点
    if (*T == NULL) {
        *T = (BTreePtr)malloc(sizeof(BTree));
        (*T)->data = e;

        return TRUE;
    } else {
        p = *T;
        while ( p != NULL) {
            //比当前节点大,则插入到右子树
            if (e > p->data) 
            {

                if (p->rchild == NULL) 
                {
                    p->rchild = (BTreePtr) malloc (sizeof(BTree));
                    p->rchild->data = e; //将节点信息存储在此右叶子节点中
                    return TRUE;
                }
                p = p->rchild;
            } 
            else  //比当前节点小,则插入到左子树
            {

                if (p->lchild == NULL)
                {
                    p->lchild = (BTreePtr) malloc (sizeof(BTree));
                    p->lchild->data = e;//将节点信息存储在此左叶子节点中
                    return TRUE;
                }
                p = p->lchild;
            }
        }
    }

    return FALSE;
}

二叉搜索树的移除,需要考虑很多情况:
1.删除的节点为叶子节点,直接删除
2.删除的节点有一个子节点,可以将该子节点作为其父节点的子节点
3.删除的节点有两个子节点,我们可以采取这样的策略:用右子树最小值代替该节点,并递归删除那个节点值。需要递归删除是因为这个最小值的节点可能还有右子树,因此需要做同样的删除操作(它不会有左子树,因为它自己的值最小)

在这里插入图片描述
删除旧值15,可以看出数值15只有一个子节点,直接将其子节点连至其父节点。

在这里插入图片描述
删除旧值12,有两个子节点,以右子树中的最小值取而代之。

int Delete(BTreePtr T, ElemType e) 
{
    BTreePtr p, pp, minP, minPP, child;
    child = NULL;
    p = T;
    pp = NULL;

    while ( (p != NULL) && (p->data != e) ) {
        pp = p;

        if (e > p->data) //比当前节点值大,从右子树查找并删除
        {
            p = p->rchild;
        } 
        else //比当前节点值小,从左子树查找并删除
        {
            p = p->lchild;
        }
    }

    if (p == NULL) return FALSE;

    //双节点
    if ((p->lchild != NULL) && (p->rchild != NULL))
    {
        minPP = p;
        minP = p->rchild;

        while (minP->lchild != NULL) {
            minPP = minP;
            minP = minP->lchild;
        }
        p->data = minP->data;
        minPP->lchild = minP->rchild;
        free(minP);

        return TRUE;
    }

    //有一个节点
    if ((p->lchild != NULL) || (p->rchild != NULL)) { //应该将原有的pp同child连接在一起

        if (p->lchild) {
            child = p->lchild;
        } else {
           child = p->rchild;
        }
        if(pp->data>p->data)
        {
            pp->lchild=child;
        } else
        {
            pp->rchild=child;
        }
        free(p);
        return TRUE;
    }

    //没有节点
    if (pp->lchild == p) {//这里面临pp除p以外的节点为null的情况
        pp->lchild = child;
    } else {
        pp->rchild = child;
    }

    return TRUE;
}

二叉搜索树 完整代码如下:

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

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef char ElemType;

typedef struct node 
{
    ElemType data;
    struct node *lchild, *rchild;
} BTree, *BTreePtr;

//插入 
Status Insert(BTreePtr *T, ElemType e) {

    BTreePtr p;

    if (*T == NULL) {
        *T = (BTreePtr)malloc(sizeof(BTree));
        (*T)->data = e;

        return TRUE;
    } else {
        p = *T;
        while ( p != NULL) 
        {

            if (e > p->data) 
            {

                if (p->rchild == NULL) 
                {
                    p->rchild = (BTreePtr) malloc (sizeof(BTree));
                    p->rchild->data = e;
                    return TRUE;
                }
                p = p->rchild;
            } 
            else 
            {

                if (p->lchild == NULL)
                {
                    p->lchild = (BTreePtr) malloc (sizeof(BTree));
                    p->lchild->data = e;
                    return TRUE;
                }
                p = p->lchild;
            }
        }
    }

    return FALSE;
}

// 删除 
Status Delete(BTreePtr T, ElemType e) {
    BTreePtr p, pp, minP, minPP, child;
    child = NULL;
    p = T;
    pp = NULL;

    while ( (p != NULL) && (p->data != e) ) 
    {
        pp = p;

        if (e > p->data) {
            p = p->rchild;
        } else {
            p = p->lchild;
        }
    }

    if (p == NULL) return FALSE;

    //双节点
    if ((p->lchild != NULL) && (p->rchild != NULL))
    {
        minPP = p;
        minP = p->rchild;

        while (minP->lchild != NULL) 
        {
            minPP = minP;
            minP = minP->lchild;
        }
        p->data = minP->data;
        minPP->lchild = minP->rchild;
        free(minP);

        return TRUE;
    }

    //有一个节点
    if ((p->lchild != NULL) || (p->rchild != NULL)) //应该将原有的pp同child连接在一起
    { 

        if (p->lchild) 
        {
            child = p->lchild;
        } else {
           child = p->rchild;
        }
        if(pp->data>p->data)
        {
            pp->lchild=child;
        } else
        {
            pp->rchild=child;
        }
        free(p);
        return TRUE;
    }

    //没有节点
    if (pp->lchild == p) //这里面临pp除p以外的节点为null的情况
    {
        pp->lchild = child;
    } else {
        pp->rchild = child;
    }

    return TRUE;
}

// 查找 

Status Find(BTreePtr T, ElemType e) 
{

    if (T == NULL) return FALSE;

    while ((T != NULL) && (T->data != e))
     {

        if (e > T->data) {
            T = T->rchild;
        } else {
            T = T->lchild;
        }
    }

    if (T) {
        return TRUE;
    } else {
        return FALSE;
    }
}


// 最大值 
ElemType FindMax(BTreePtr T) 
{
    ElemType max;

    while(T != NULL) 
    {
        max = T->data;
        T = T->rchild;
    }
    return max;
}


//最小值 
ElemType FindMin(BTreePtr T) 
{
    ElemType min;

    while(T != NULL) 
    {
        min = T->data;
        T = T->lchild;
    }
    return min;
}


void PreOrderTraverse(BTreePtr T)//前序遍历二叉树
{
    if (T == NULL) return;

    if(T)
    {
        printf("%d ",T->data);
        PreOrderTraverse(T->lchild);
        PreOrderTraverse(T->rchild);
    }
}


void DestroyTree(BTreePtr T) {
    if (T)
    {
        if (T->lchild)
        {
            DestroyTree(T->lchild);
        }

        if(T->rchild)
        {
            DestroyTree(T->rchild);
        }

        free(T);
        T = NULL;
    }
}


int main(int argc, char const *argv[])
{
    BTreePtr T;
    T = NULL;
    int a[] = {33, 16, 50, 13, 18, 34, 58, 15, 17, 25, 51, 66, 19, 27, 55};
    int i;
    for (i = 0; i < 15; i++) {
        Insert(&T, a[i]);
    }
    printf("Max is %d\n", FindMax(T));
    printf("Min is %d\n", FindMin(T));
    Delete(T, 18);
    Delete(T, 13);
    PreOrderTraverse(T);
    DestroyTree(T);

    return 0;
}

输出结果:
在这里插入图片描述
在这里插入图片描述
扫二维码关注微信公众号,获取技术干货

参考:STL 源码解析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值