數據結構-二叉排序樹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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章