实现二叉查找树 -- 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;
}

图示:
《实现二叉查找树 -- C语言》
《实现二叉查找树 -- C语言》

完整代码:

头文件代码:

// 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;
}

    原文作者:二叉查找树
    原文地址: https://blog.csdn.net/u014235934/article/details/50924876
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞