数据结构-二叉树(binaryTree)-C语言实现

用C语言实现二叉树基本功能

接口部分 “binaryTree.h”
/*二叉树接口*/

#ifndef _BINARY_TREE_H_
#define _BINARY_TREE_H_
#include <stdbool.h>

#define NAME_LENGTH 20
#define MAX_TRNODES 15  //测试用定义的最大节点数量,根据情况设定

/*实体定义*/
typedef struct _item
{
    char name[NAME_LENGTH];  //名字作为主键
    int value;
} Item;

typedef struct _trnode
{
    struct _trnode *left;
    struct _item item;
    struct _trnode *right;
} Trnode;

typedef struct _tree
{
    struct _trnode *root;
    int total;
} Tree;


/*函数原型*/
//初始化树
void InitializeTree(Tree *tree);

//树是空的吗
bool TreeIsEmpty(const Tree *tree);
//没有子节点吗
bool TreeHasEmpty(const Tree *tree);
//树是满的吗
bool TreeIsFull(const Tree *tree);
//有两个子节点吗
bool TreeHasFull(const Tree *tree);
//获得树的项数
int TrnodesCount(const Tree *tree);

//新建一个树节点, 传入 item 储存在节点中
bool AddTrnode(Tree *tree, Item item);
//查找一个树节点, 传入 item 进行比对, 查找的节点地址存入 pnode , 相应父节点地址存入 plast 
bool SearchTrnode(const Tree *tree, Item item, Trnode **pnode, Trnode **plast);
//删除一个树节点, 传入 item 利用 查找函数 获得目标地址, 对目标空间进行释放(free)
bool DeleteTrnode(Tree *tree, Item item);
//打印节点, 通过 递归 将信息输出到屏幕
void printTrnode(const Tree *tree);

//删除所有树节点, 递归 释放(free) 节点空间
void DeleteAllTrnode(Tree *tree);

#endif
实现部分 “binaryTree.c”
初始化
void InitializeTree(Tree *tree)
{
    tree->root = NULL;  //根节点置空
    tree->total = 0;    //节点数归零
}
获得树的状态

bool TreeIsEmpty(const Tree *tree)
{
    if (NULL == tree->root) {  //根节点为空则树为空
        return true;
    } else {
        return false;
    }
}

bool TreeHasEmpty(const Tree *tree)
{
    if ( NULL == (tree->root)->left && NULL == (tree->root)->right ) {
        return true;  //左右节点都为空
    } else {
        return false;
    }
}

bool TreeHasFull(const Tree *tree) 
{
    if ( NULL != (tree->root)->left && NULL != (tree->root)->right ) {
        return true;  //左右节点都不为空
    } else {
        return false;
    }
}

bool TreeIsFull(const Tree *tree)
{
    if (MAX_TRNODES == tree->total) {
        return true;  //节点树是否达到上限
    } else {
        return false;
    }
}

int TrnodesCount(const Tree *tree)
{
    return tree->total;  返回节点数
}
增加一个节点
  • 树满了吗, 满了返回 false
  • 树为空时, 节点作为根节点
  • 树不为空时
    • 比较两个字符串, 判断接下来往左走还是往右走 ( 两边都不对说明这个值是重复的, 返回 false)
    • 看接下来在这个方向上是空位吗, 是则把节点加到这, 不是则继续往下走
  • 计数器加一
/*增加节点*/
bool AddTrnode(Tree *tree, Item item)
{
    //申请一块新内存
    Trnode *p = (Trnode*) malloc(sizeof(Trnode));

    //空间有效
    if (NULL != p) {
        p->item = item;
        if (TreeIsFull(tree)) {
            fprintf(stderr, "Sorry, space is full.\n");  //空间已满
            return false;
        } else {
            if (TreeIsEmpty(tree)) {  //空树
                tree->root = p;
                p->left = NULL;
                p->right = NULL;
            } else {
                //找到节点添加的位置
                Trnode *temp = tree->root;
                do {
                    //toLeft(toRight): 自定义函数, 比较两个字符串, 确实往左(右)走时返回 true
                    if (toLeft((temp->item).name, item.name)) {  //往左走
                        if (temp->left != NULL) {
                            temp = temp->left;  //继续往下遍历
                        } else {
                            temp->left = p;  //添加节点
                            p->left = NULL;
                            p->right = NULL;
                            break;
                        }
                    }
                    else if (toRight((temp->item).name, item.name)) {  //往右走
                        if (temp->right != NULL) {
                            temp = temp->right;
                        } else {
                            temp->right = p;
                            p->left = NULL;
                            p->right = NULL;
                            break;
                        }
                    }
                    else {  //主关键字重复
                        fprintf(stderr, "Sorry, name: %s already exists.\n", item.name);
                        return false;
                    }
                }while (temp != NULL);
            }
        }
    }
    tree->total++;
    return true;
}
查找节点
  • 空树返回 false
  • plast 保存上一层循环的节点地址
  • pnode 根据判断的结果不断往下更新
  • 直到比较结果为两节点相同时, 比较结束. 此时 plast 存的是 pnode 的父节点, pnode 为查找的结果
  • 如果查找到最后还是没有找到相同的节点, 返回 false
/*查找节点*/
bool SearchTrnode(const Tree *tree, Item item, Trnode **pnode, Trnode **plast)
{
    Trnode *temp = tree->root;
    char name[NAME_LENGTH];
    strcpy(name, item.name);

    if (TreeIsEmpty(tree)) {  //为空
        fprintf(stderr, "Empty tree, no data.\n");
        return false;
    } else {  //不为空
        do {
            if (toLeft((temp->item).name, name)) {
                *plast = temp;  //记录上一个节点的地址
                temp = temp->left;
            }
            else if (toRight((temp->item).name, name)) {
                *plast = temp;  //记录上一个节点的地址
                temp = temp->right;
            }
            else {
                *pnode = temp;  //返回当前节点的地址
                return true;
            }
        }while (NULL != temp);  //子节点为空说明查找结束
    }
    return false;
}
删除节点
  • 使用查找函数找到待删除节点及其父节点的位置, 找不到返回 false
  • 删除时分三种情况考虑

    • 待删除节点没有子树时

      • 将父节点对应位置(左边/右边)置空
    • 待删除节点有两个子树时

      • 待删除节点是父节点的左子树
        • 将右子树作为父节点的左子树
        • 将左子树接到右子树的左支最下端的左端
      • 待删除节点是父节点的右子树
        • 相反
    • 待删除节点只有一个子树时
      • 将待删除节点的子树移动为父节点的子树
    • 上面三种情况都要分别考虑待删除节点为树的根节点的情况 ( 即 待删除节点的父节点为空时 )
  • 释放待删除节点空间
  • 计数器减一
/*删除节点*/
bool DeleteTrnode(Tree *tree, Item item)
{
    Trnode *pnode = NULL, *plast = NULL;
    Trnode *pleft = NULL, *pright = NULL;
    if ( !SearchTrnode(tree, item, &pnode, &plast) ) {
        return false;
    }

    Tree tempTree;
    InitializeTree(&tempTree);
    tempTree.root = pnode;

    //没有子树时
    if (TreeHasEmpty(&tempTree)) {
        //删除的是根节点
        if (NULL == plast) {
            tree->root = NULL;
        } else {  //父节点的相应子节点归空
            if (atLeft(plast, pnode)) {
                plast->left = NULL;
            } else {
                plast->right = NULL;
            }
        }
    }
    //左右子树都有
    else if (TreeHasFull(&tempTree)) {
        //储存当前节点的左右子树地址
        pleft = pnode->left;
        pright = pnode->right;
        //删除的是根节点
        if (NULL == plast) {
            //将左子树根节点作为主树根节点
            tree->root = pleft;
            //将右子树接到左子树的右支最下端的最右端
            tempTree.root = pleft;
            toRightEnd(&tempTree);
            (tempTree.root)->right = pright;
        } else {
            //在左边
            if (atLeft(plast, pnode)) {
                //将右子树作为父节点的左子树
                plast->left = pright;
                //将左子树接到右子树的左支最下端的最左端
                tempTree.root = pright;
                toLeftEnd(&tempTree);
                (tempTree.root)->left = pleft;
            } else {  //在右边
                //将左子树作为父节点的右子树
                plast->right = pleft;
                //将右子树接到左子树的右支最下端的最右端
                tempTree.root = pleft;
                toRightEnd(&tempTree);
                (tempTree.root)->right = pright;
            }
        }
    }
    //左右子树只有一个
    else {
        //删除的是根节点
        if (NULL == plast) {
            if (NULL != pnode->left) {
                tree->root = pnode->left;
            } else {
                tree->root = pnode->right;
            }
        } else {
            //确定有的是左子树还是右子树
            if (atLeft(plast, pnode)) {
                //将其作为父节点的子树
                if (NULL != pnode->left) {
                    plast->left = pnode->left;
                } else {
                    plast->left = pnode->right;
                }
            } else {
                if (NULL != pnode->left) {
                    plast->right = pnode->left;
                } else {
                    plast->right = pnode->right;
                }
            }
        }
    }
    free(pnode);
    (tree->total)--;
    return true;
}
打印节点到屏幕
  • 为空树时, 不打印
  • 不为空树
    • 有子树吗, 没有则打印当前节点, 返回上一层
    • 往左往右递归
    • 打印当前节点, 返回上一层
/*打印节点*/
void printTrnode(const Tree *tree)
{
    if (TreeIsEmpty(tree)) {
        fprintf(stderr, "Empty tree, no data.\n");
        return;
    }

    //创建临时树作为参数树的拷贝
    Tree tempTree;
    InitializeTree(&tempTree);
    tempTree = *tree;

    //记录临时树的根节点
    Trnode *p = tempTree.root;

    //递归遍历
    if (TreeHasEmpty(&tempTree)) {  //无路可走,返回上一层
        printf("%-20s\t %-10d\t %-15p\t %-15p\t %-15p\n",(p->item).name, (p->item).value, p, p->left, p->right);
        return ;
    } else {
        if (p->left != NULL) {  //左边有路,往左走
            tempTree.root = p->left;
            printTrnode(&tempTree);
        }
        if (NULL != p->right) {  //右边有路,往右走
            tempTree.root = p->right;
            printTrnode(&tempTree);
        }
    }

    //递归处理节点都在递归后(返回前)处理, 或者在递归前处理, 避免重复调用
    printf("%-20s\t %-10d\t %-15p\t %-15p\t %-15p\n",(p->item).name, (p->item).value, p, p->left, p->right);
    return ;
}
删除所有节点
  • 与打印节点相似, 使用递归逐一删除节点, 为空树返回 false
/*删除所有节点*/
void DeleteAllTrnode(Tree *tree)
{
    freeTrnodes(tree);
    tree->root = NULL;
    tree->total = 0;
}

static void freeTrnodes(Tree *tree)
{
    if (TreeIsEmpty(tree)) {
        fprintf(stderr, "Empty tree, no data.\n");
        return;
    }

    //创建临时树作为参数树的拷贝
    Tree tempTree;
    InitializeTree(&tempTree);
    tempTree = *tree;

    //记录临时树的根节点
    Trnode *p = tempTree.root;

    //递归遍历
    if (TreeHasEmpty(&tempTree)) {  //无路可走,返回上一层
        free(p);
        return ;
    } else {
        if (p->left != NULL) {  //左边有路,往左走
            tempTree.root = p->left;
            freeTrnodes(&tempTree);
        }
        if (NULL != p->right) {  //右边有路,往右走
            tempTree.root = p->right;
            freeTrnodes(&tempTree);
        }
    }

    //递归处理节点都在递归后(返回前)处理, 或者在递归前处理, 避免重复调用
    free(p);
    return ;
}
写一个模块测试一个模块

我的完整代码(含测试)

没有更多推荐了,返回首页