数据结构-二叉排序树BST初探

首先给出二叉排序树(Binary Sort Tree)的定义:

 一棵二叉排序树或者是一棵空树或者满足以下条件:
 (1)若它的左子树不为空,则左子树所有节点的值均小于根的值
 (2)若它的右子树不为空,则右子树所有节点的值均大于根的值
 (3)左右子树本身又分别是二叉排序树

如下图就是一个二叉排序树:(绘画水平真的就这样了,不要在意这些细节)
这里写图片描述

由二叉排序树的定义可以看出来,其实二叉排序树可以看做是一个有序表,对每一个结点来说它的左子树比根小,右子树比根大。从小到大的顺序是左根右,是不是和中序遍历很像,对二叉排序树的中序遍历就是从小到大的排序,所以可以把它看成是一个有序表。

为了在存储这样的二叉树,使用的结构体如下:

typedef struct node
{
    int key;
    struct node *lchild, *rchild;
}BSTree;

其中key就是二叉树结点的值
*lchild, *rchild分别指向左孩子和右孩子

1.那么如何构造一个二叉排序树呢?

    我们可以想得到,我们需要的是排序二叉树根结点的指针,和我们需要插入的值,所以就将这两个值作为函数的参数。
    其次,我们需要根据根结点的指针,找到合适的插入位置,然后创建一个结点p,结点p的值就是传进来要添加的参数,结点p的左右孩子都为NULL。然后把它插入到这个位置上去。

具体程序如下:

//二叉排序树插入元素
void Insert_BSTree(BSTree *t, int k)
{
    //用q来记录插入的位置,p来记录插入位置的双亲
    BSTree *p, *q;
    bool end = false;
    q = t;

    //在二叉树中找适合的插入位置
    while (q != NULL)
    {
        if (k == q->key) //如果加入的值已经存在就直接退出
        {
            end = true;
        }
        else
        {
            if (k < q->key)
            {
                p = q;
                q = q->lchild;
            }
            else
            {
                p = q;
                q = q->rchild;
            }
        }
    }

    //如果需要插入
    if (!end)
    {
        q = (BSTree*)malloc(sizeof(BSTree));
        q->key = k;
        q->lchild = NULL;
        q->rchild = NULL;

        if (p->key < k) //如果插入值大于根
        {
            p->rchild = q;
        }
        else 
        {
            p->lchild = q;
        }
    }
}
有了插入函数之后,就根据结点的个数进行插入就可以了。创建如下:
//创建二叉排序树
void Create_BDTree(BSTree *t)
{
    int i, n, x;
    printf("请输入需要插入的个数:");
    scanf("%d", &n);
    printf("请输入%d个需要插入的值:\n", n);
    scanf("%d", &x);
    //为第一个结点赋值
    t->key = x;
    t->lchild = NULL;
    t->rchild = NULL;
    for (i = 0; i < n-1; i++) //将剩下的n-1个结点插入
    {
        scanf("%d", &x);
        Insert_BSTree(t, x);
    }
    printf("创建完成\n");
}

2.查找值

二叉树的一个重要功能就是查找,因为它的性质,它的查找就好像是折半查找一样,每次可以根据大小的比较来缩小查找的范围。
具体的程序如下:
//查找二叉排序树的节点
BSTree* Search_BSTree(BSTree *t, int k)
{
    while (t != NULL)
    {
        if (k == t->key) //找到了
            return t;
        else
        {
            if (k < t->key)
            {
                t = t->lchild;
            }
            else
            {
                t = t->rchild;
            }
        }
    }
    return NULL; //没有找到
}

3.删除一个结点

    因为二叉排序树是动态查找,它相比于折半查找的好处就是它可以很方便的删除一个结点。
    删除结点时,由于结点的不同位置有不同的删除方法,所以删除的难度相比于以上的运算都要来的复杂一些。具体分为以下四种情况:


(1)删除的结点是叶子结点
    这种情况很简单,可以直接删除就好了

这里写图片描述

(2)删除的结点只有左子树
    用该右子树的根取代待删结点的位置

这里写图片描述

(3)删除的结点只有右子树
    用该左子树的根取代待删结点的位置

这里写图片描述

(4)删除的结点既有右子树又有左子树
    需要用待删结点在二叉排序树中序遍历中直接后继结点来取代该待删结点,然后直接后继结点在按照以上三中情况处理。

这里写图片描述

    在这里说明一下28号结点,首先这个结点不会有左孩子,如果有,他就不会是根结点的中序遍历后继了,而是他的左孩子是。
    所以28结点只有两种情况:没有右孩子、有右孩子。
    对于没有右孩子(如图),就执行情况1,将33结点的左孩子置空。
    如果有右孩子,执行情况2,用该结点的右子树的根代替该结点。

删除程序如下:

//删除二叉排序树节点
void delete_BSTree(BSTree *t, int k)
{
    BSTree *temp, *parent, *next;
    temp = t;
    parent = NULL;

    //在二叉树中寻找给定值的节点
    while (temp != NULL)
    {
        if (temp->key == k)
        {
            break;
        }
        else if (temp->key < k)
        {
            parent = temp;
            temp = temp->rchild;
        }
        else if (temp->key > k)
        {
            parent = temp;
            temp = temp->lchild;
        }
    }

    if (temp == NULL)
    {
        printf("没有找到要删除的点");
    }
    else
    {
        if (temp->lchild == NULL && temp->rchild == NULL)
        {
            //第一种情况:删除叶子结点
            if (parent->lchild == temp)
            {
                parent->lchild = NULL;
            }
            else
            {
                parent->rchild = NULL;
            }
        }
        else if (temp->lchild == NULL)
        {
            //第二种情况:只有右子树
            if (parent->lchild == temp)
            {
                parent->lchild = temp->rchild;
            }
            else
            {
                parent->rchild = temp->rchild;
            }
        }
        else if (temp->rchild == NULL)
        {
            //第三种情况:只有左子树
            if (parent->lchild == temp)
            {
                parent->lchild = temp->lchild;
            }
            else
            {
                parent->rchild = temp->lchild;
            }
        }
        else
        {
            //第四种情况:有右子树,又有左子树
            next = temp->rchild;
            while (next->lchild != NULL)
            {
                parent = next;
                next = next->lchild;
            }

            temp->key = next->key;
            if (parent->lchild == next)
            {
                parent->lchild = next->rchild;
            }
            else
            {
                parent->rchild = next->rchild;
            }
        }
    }
}

最后我们使用中序遍历一下该树:

//输出二叉排序树
void ShowData_BSTree(BSTree *t)
{
    if (t != NULL)
    {   
        ShowData_BSTree(t->lchild);
        printf("%3d", t->key);
        ShowData_BSTree(t->rchild);
    }
}

4.实验结果

这里写图片描述

5.源程序

/*
 * 测试二叉排序树
 */

#include <stdio.h>
#include <malloc.h>

typedef struct node
{
    int key;
    struct node *lchild, *rchild;
}BSTree;

//二叉排序树插入元素
void Insert_BSTree(BSTree *t, int k)
{
    //用q来记录插入的位置,p来记录插入位置的双亲
    BSTree *p, *q;
    bool end = false;
    q = t;

    //在二叉树中找适合的插入位置
    while (q != NULL)
    {
        if (k == q->key) //如果加入的值已经存在就直接退出
        {
            end = true;
        }
        else
        {
            if (k < q->key)
            {
                p = q;
                q = q->lchild;
            }
            else
            {
                p = q;
                q = q->rchild;
            }
        }
    }

    //如果需要插入
    if (!end)
    {
        q = (BSTree*)malloc(sizeof(BSTree));
        q->key = k;
        q->lchild = NULL;
        q->rchild = NULL;

        if (p->key < k) //如果插入值大于根
        {
            p->rchild = q;
        }
        else 
        {
            p->lchild = q;
        }
    }
}

//创建二叉排序树
void Create_BDTree(BSTree *t)
{
    int i, n, x;
    printf("请输入需要插入的个数:");
    scanf("%d", &n);
    printf("请输入%d个需要插入的值:\n", n);
    scanf("%d", &x);
    t->key = x;
    t->lchild = NULL;
    t->rchild = NULL;
    for (i = 0; i < n-1; i++)
    {
        scanf("%d", &x);
        Insert_BSTree(t, x);
    }
    printf("创建完成\n");
}

//查找二叉排序树的节点
BSTree* Search_BSTree(BSTree *t, int k)
{
    while (t != NULL)
    {
        if (k == t->key)
            return t;
        else
        {
            if (k < t->key)
            {
                t = t->lchild;
            }
            else
            {
                t = t->rchild;
            }
        }
    }
    return NULL;
}

//删除二叉排序树节点
void delete_BSTree(BSTree *t, int k)
{
    BSTree *temp, *parent, *next;
    temp = t;
    parent = NULL;

    //在二叉树中寻找给定值的节点
    while (temp != NULL)
    {
        if (temp->key == k)
        {
            break;
        }
        else if (temp->key < k)
        {
            parent = temp;
            temp = temp->rchild;
        }
        else if (temp->key > k)
        {
            parent = temp;
            temp = temp->lchild;
        }
    }

    if (temp == NULL)
    {
        printf("没有找到要删除的点");
    }
    else
    {
        if (temp->lchild == NULL && temp->rchild == NULL)
        {
            //第一种情况:删除叶子结点
            if (parent->lchild == temp)
            {
                parent->lchild = NULL;
            }
            else
            {
                parent->rchild = NULL;
            }
        }
        else if (temp->lchild == NULL)
        {
            //第二种情况:只有右子树
            if (parent->lchild == temp)
            {
                parent->lchild = temp->rchild;
            }
            else
            {
                parent->rchild = temp->rchild;
            }
        }
        else if (temp->rchild == NULL)
        {
            //第三种情况:只有左子树
            if (parent->lchild == temp)
            {
                parent->lchild = temp->lchild;
            }
            else
            {
                parent->rchild = temp->lchild;
            }
        }
        else
        {
            //第四种情况:有右子树,又有左子树
            next = temp->rchild;
            while (next->lchild != NULL)
            {
                parent = next;
                next = next->lchild;
            }

            temp->key = next->key;
            if (parent->lchild == next)
            {
                parent->lchild = next->rchild;
            }
            else
            {
                parent->rchild = next->rchild;
            }
        }
    }
}

//输出二叉排序树
void ShowData_BSTree(BSTree *t)
{
    if (t != NULL)
    {   
        ShowData_BSTree(t->lchild);
        printf("%3d", t->key);
        ShowData_BSTree(t->rchild);
    }
}

int main()
{
    int n;
    BSTree *result;
    BSTree *t = (BSTree*)malloc(sizeof(BSTree));
    Create_BDTree(t);
    printf("\n二叉排序树中序遍历如下:\n");
    ShowData_BSTree(t);
    printf("\n\n");
    printf("请输入需要查找的值:");
    scanf("%d", &n);
    result = Search_BSTree(t, n);
    if (result == NULL)
    {
        printf("没有查找到该值\n");
    }
    else
    {
        printf("查找成功,查找的值为%d,", result->key);
        if (result->lchild)
        {
            printf("其左孩子编号为%d,", result->lchild->key);
        }
        else
        {
            printf("其没有左孩子,");
        }
        if (result->rchild)
        {
            printf("其右孩子编号为%d\n", result->rchild->key);
        }
        else
        {
            printf("其没有右孩子\n");
        }
    }
    printf("请输入需要删除的值:");
    scanf("%d", &n);
    delete_BSTree(t, n);
    printf("\n二叉排序树中序遍历如下:\n");
    ShowData_BSTree(t);
    printf("\n\n");
    getchar();
    getchar();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章