實現二叉查找樹 -- C語言

本文章參考了《數據結構與算法分析——C語言描述》這本書。書中寫的還是挺透徹的

首先介紹一下二叉樹。什麼是二叉樹呢?

二叉樹(binary tree)是一棵樹,其每個節點最多能有兩個兒子。由於整個特性,實現二叉樹的方式是創建兩個指針分別指向左兒子和右兒子。

二叉查找樹是一種特殊的二叉樹,其特點是:對於樹的每個節點x,其左子樹中所有關鍵字的值都小於x, 其右子樹中所有關鍵字的值都大於x。其左右子樹也都是二叉查找樹,並且在二叉查找樹中沒有重複的鍵值。


首先是聲明二叉查找樹:

#ifndef _TREE_H_

typedef int ElementType;
typedef struct treeNode *Position;
typedef struct treeNode *SearchTree;
struct treeNode
{
    ElementType Element;
    SearchTree lch;
    SearchTree rch;
};

SearchTree MakeEmpty(SearchTree T);
Position Find(SearchTree T, ElementType x);
Position FindMin(SearchTree T);
Position FindMax(SearchTree T);
SearchTree Insert(SearchTree T, ElementType x);
SearchTree Delete(SearchTree T, ElementType x);
void PrintTree(SearchTree T);
void Destroy(SearchTree T);

#endif // _TREE_H_


對於二叉查找樹主要有以下幾個操作:

1.Find查找操作

這個操作返回指向樹T中具有關鍵字X的節點的指針,如果不存在就返回NULL

樹的結構令這種操作變得十分簡單,只需要令X與當前節點比較就可以了,如果X小於當前節點,就用遞歸的方式去當前節點的左子樹尋找,如果x大於當前節點就去右子樹查找。如果相等就返回當前節點的指針,如果找不到就返回NULL;

Position Find(SearchTree T, ElementType x)
{
    if(T == NULL)
        return NULL;
    if(T->Element > x)
        return Find(T->lch, x);
    else if(T->Element < x)
        return Find(T->rch, x);
    else
        return T;
}

2.尋找最小值FindMin操作與尋找最大值FindMax操作

根據二叉查找樹的特點可以發現其最小值總是在最左邊的子樹,其最大值在最右邊的子樹。

// 遞歸實現
Position FindMin(SearchTree T)
{
    if(T == NULL)
        return NULL;        // 考慮是空樹的情況
    if(T->lch == NULL)
        return T;
    else
        return FindMin(T->lch);
}

// 非遞歸實現
Position FindMax(SearchTree T)
{
    if(T != NULL)
        while(T->rch != NULL)
            T = T->rch;

    return T;
}

3.插入Inset操作

插入操作也比較簡單,像find一樣在樹中尋找插入的位置,將X插入該路徑最後的位置,如果X一存在,則說明也不用做

SearchTree Insert(SearchTree T, ElementType x)
{
    if(T == NULL)
    {
        T = (struct treeNode *)malloc(sizeof(struct treeNode));
        T->Element = x;
        T->lch = T->rch = NULL;
    }
    else if(x < T->Element)
        T->lch = Insert(T->lch, x);
    else if(x > T->Element)
        T->rch = Insert(T->rch, x);
    else if(x == T->Element)
        cout << "數據已存在!\n";

    return T;
}

4.刪除Delete操作

刪除是比較複雜的操作,有多種情況要考慮的

1>如果要刪除的節點是葉子節點,則直接刪除就可以了

2>如果刪除的節點又一個兒子,就讓其父節點繞過這個字節就可以了

3>比較麻煩的是要刪除的節點又兩個子節點。我所採用的策略是將其右節點最小的數據來代替該節點的數據,然後將那個被代替的數據給刪除

SearchTree Delete(SearchTree T, ElementType x)
{
    Position TemCell;

    if(T == NULL)
        cout << "沒有該數據,刪除失敗!\n";
    else if(x < T->Element)
        T->lch = Delete(T->lch, x);
    else if(x > T->Element)
        T->rch = Delete(T->rch, x);
    else if(T->lch && T->rch)   // 有連個子節點的情況
    {
        TemCell = FindMin(T->rch);
        T->Element = TemCell->Element;
        T->rch = Delete(T->rch, T->Element);
    }
    else                        // 有一個子節點或者沒有子節點的情況下
    {
        TemCell = T;
        if(T->lch == NULL)
            T = T->rch;
        else if(T->rch == NULL)
            T = T->lch;
        free(TemCell);
        cout << "數據刪除成功!\n";
    }

    return T;
}
圖示:




完整代碼:

頭文件代碼:

// tree.h
// binary search tree
// 二叉搜索樹,也稱二叉查找樹,有序二叉樹
// 使二叉樹成爲二叉查找樹的性質是:
// 對於樹的每個節點x,其左子樹中所有關鍵字的值都小於x;
// 其右子樹中所有關鍵字的值都大於x。
// 由於樹的遞歸定義,所以通常是遞歸的操作,
// 因爲二叉查找樹的平均深度爲O(log N),所以一般不必擔心棧空間被用盡


// 二叉查找樹的聲明
#ifndef _TREE_H_

typedef int ElementType;
typedef struct treeNode *Position;
typedef struct treeNode *SearchTree;
struct treeNode
{
    ElementType Element;
    SearchTree lch;
    SearchTree rch;
};

SearchTree MakeEmpty(SearchTree T);
Position Find(SearchTree T, ElementType x);
Position FindMin(SearchTree T);
Position FindMax(SearchTree T);
SearchTree Insert(SearchTree T, ElementType x);
SearchTree Delete(SearchTree T, ElementType x);
void PrintTree(SearchTree T);
void Destroy(SearchTree T);

#endif // _TREE_H_




實現部分:

// ree.cpp
// 二叉查找樹實現
#include <iostream>
#include <cstdio>
#include "tree.h"
using namespace std;

// 建立一棵空樹
// 利用遞歸將所有子樹的地址都釋放,返回一個空指針
SearchTree MakeEmpty(SearchTree T)
{
    if(T != NULL)
    {
        MakeEmpty(T->lch);
        MakeEmpty(T->rch);
        delete T;
    }
    return T;
}

// 二叉查找樹的Find操作
// 比較x的值與當前節點的值,如果x小,就往左子樹查找,如果x大就往右子樹查找
// 如果沒有找到就返回空,找到就返回x的地址
Position Find(SearchTree T, ElementType x)
{
    if(T == NULL)
        return NULL;
    if(T->Element > x)
        return Find(T->lch, x);
    else if(T->Element < x)
        return Find(T->rch, x);
    else
        return T;
}

// 二叉查找樹的FindMin操作
// 根據二叉查找樹的特性,最左邊的子樹就是其最小值
// 遞歸實現
Position FindMin(SearchTree T)
{
    if(T == NULL)
        return NULL;        // 考慮是空樹的情況
    if(T->lch == NULL)
        return T;
    else
        return FindMin(T->lch);
}

// 二叉查找樹的FindMax操作
// 根據二叉查找樹的特性,最右邊的子樹就是其最大值
// 非遞歸實現
Position FindMax(SearchTree T)
{
    if(T != NULL)
        while(T->rch != NULL)
            T = T->rch;

    return T;
}

// 二叉查找樹的Inset操作
// 像Find一樣遍歷查找。如果找到x,則給出提示,否則將x插入到遍歷的最後一點上
SearchTree Insert(SearchTree T, ElementType x)
{
    if(T == NULL)
    {
        T = (struct treeNode *)malloc(sizeof(struct treeNode));
        T->Element = x;
        T->lch = T->rch = NULL;
    }
    else if(x < T->Element)
        T->lch = Insert(T->lch, x);
    else if(x > T->Element)
        T->rch = Insert(T->rch, x);
    else if(x == T->Element)
        cout << "數據已存在!\n";

    return T;
}

// 二叉查找樹的Delete的實現
// 分三種情況進行考慮:1.如果節點是一片葉子,則直接刪除就可以了
//                     2.如果節點只有一個兒子,就繞過這個節點把這個節點刪除就可以
//                     3.如果幾點有兩個節點就比較複雜了,一般的策略是找到其右子樹最小的數據
//                       代替該節點的數據並遞歸的刪除那個被代替的節點
SearchTree Delete(SearchTree T, ElementType x)
{
    Position TemCell;

    if(T == NULL)
        cout << "沒有該數據,刪除失敗!\n";
    else if(x < T->Element)
        T->lch = Delete(T->lch, x);
    else if(x > T->Element)
        T->rch = Delete(T->rch, x);
    else if(T->lch && T->rch)   // 有連個子節點的情況
    {
        TemCell = FindMin(T->rch);
        T->Element = TemCell->Element;
        T->rch = Delete(T->rch, T->Element);
    }
    else                        // 有一個子節點或者沒有子節點的情況下
    {
        TemCell = T;
        if(T->lch == NULL)
            T = T->rch;
        else if(T->rch == NULL)
            T = T->lch;
        free(TemCell);
        cout << "數據刪除成功!\n";
    }

    return T;
}

// 中序遍歷
void PrintTree(SearchTree T)
{
    if(T != NULL)
    {
        PrintTree(T->lch);
        cout << T->Element << " ";
        PrintTree(T->rch);
    }
}

// 銷燬二叉查找樹
void Destroy(SearchTree T)
{
    if(T->lch != NULL)
        Destroy(T->lch);
    if(T->rch != NULL)
        Destroy(T->rch);

    free(T);
}

測試程序:

// usetree.cpp 於tree.cpp一起編譯
// 二叉查找樹測試程序

#include <iostream>
#include "tree.h"
using namespace std;

int main()
{
    SearchTree T = NULL;
    Position p;
    int cmd;
    MakeEmpty(T);
    do
    {
        int x;
        cout << "菜單:\t1.插入數據\n"
             << "\t2.查找數據\n"
             << "\t3.查找最小值\n"
             << "\t4.查找最大值\n"
             << "\t5.刪除數據\n"
             << "\t6.遍歷二叉查找樹\n"
             << "\t0.銷燬二叉查找樹並退出程序\n"
             << "輸入命令:";
        cin >> cmd;
        switch(cmd)
        {
        case 1:
            cout << "請輸入插入的數據:";
            cin >> x;
            T = Insert(T, x);
            break;
        case 2:
            cout << "請輸入插入的數據:";
            cin >> x;
            p =Find(T, x);
            if(p != NULL)
                cout << "數據存在!\n";
            else
                cout << "數據未找到!\n";
            break;
        case 3:
            p = FindMin(T);
            if(p != NULL)
                cout << "最小值爲:" << p->Element;
            else
                cout << "當前爲空樹!";
            cout << endl;
            break;
        case 4:
            p = FindMax(T);
            if(p != NULL)
                cout << "最大值:" << p->Element;
            else
                cout << "當前爲空樹!";
            cout << endl;
            break;
        case 5:
            cout << "請輸入要刪除的數據:";
            cin >> x;
            T = Delete(T, x);

            break;
        case 6:
            if(T != NULL)
                PrintTree(T);
            else
                cout << "當前爲空樹!";
            cout << endl;
            break;
        }
    }while(cmd != 0);
    Destroy(T);

    return 0;
}







發佈了42 篇原創文章 · 獲贊 15 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章