C语言 实现一个二叉树

1、概述

二叉树是一种特殊的树,每个父节点最多只有2个直接子孩子,每个节点也只有一个直接父节点(根节点默认无父节点)。我们实现类似下图所示的二叉树(图片来源网络):
这里写图片描述

可以看出,该树满足一般二叉树的特性:

  • 包含一个树根节点
  • 每个节点都最多只有2个直接子孩子
  • 每个子孩子也最多只有一个父节点
  • 随着树深度的增加,所能容纳的元素也是成倍增长。宽度呈指数级增长,高度增长相对较缓慢
  • 等等

对二叉树一般包含如下组成:

  • 树根节点
  • 树的大小(通常指元素个数)
  • 用于节点元素比较的函数指针域
  • 其他树操作的函数指针(指向具体的操作函数)
  • 等等

我们可以使用如下结构来表示

typedef struct BinaryTree
{
    int size; // 大小
    BTNode* root;  // 树根节点
    void(* destory)(void* data);  // 用于释放节点数据内存的自定义函数
    int (* compare)(const void* arg0, const void* arg1); // 用于比较数据大小的自定义函数

    // pointers 省略部分函数指针声明..

} BTree;

同时,每个树拥有很多个节点,通常包含左右孩子指针域及数据域,对于树的节点我们可以使用如下结构来定义,:

typedef struct BinaryTreeNode
{
    struct BinaryTreeNode* left; // 左子节点
    struct BinaryTreeNode* right;  // 右子节点
    void* data;  // 存储的数据
} BTNode;

结构体不熟悉可以参考:C语言 结构体与共用体

2、实现

2.1 头文件实现

2.1.1 定义树结构、树节点结构

在BinaryTree.h中定义如下:

// 定义树节点
typedef struct BinaryTreeNode
{
    struct BinaryTreeNode* left; // 左子节点
    struct BinaryTreeNode* right;  // 右子节点
    void* data;  // 存储的数据
} BTNode;

// 定义树结构
typedef struct BinaryTree
{
    int size; // 大小
    BTNode* root;
    void(* destory)(void* data);  // 用于释放节点数据内存的自定义函数
    int (* compare)(const void* arg0, const void* arg1); // 用于比较数据大小的自定义函数

    // pointers
    void(*BTreeInit)(struct BinaryTree* tree, void(*destory)(void* data));

    void(*BTreeInitWithCompare)(struct BinaryTree* tree, int(*compare)(const void* arg0, const void* arg1));

    void(*BTreeDestory)(struct BinaryTree* tree);

    struct BinaryTree* (*createBTree)(void(*destory)(void* data));

    int(*insertRight)(struct BinaryTree*  tree, BTNode* target, const void* data);

    int(*insert)(struct BinaryTree*  tree, const void* data);

    int(*insertLeft)(struct BinaryTree*  tree, BTNode* target, const void* data);

    int(*removeLeft)(struct BinaryTree*  tree, BTNode* target);  // 删除左子树

    int(*removeRight)(struct BinaryTree*  tree, BTNode* target);  // 删除右子树

    int(*getSize)(struct BinaryTree* tree);

    BTNode* (*getRootNode)(struct BinaryTree* tree);

    int(*isEndOfTree)(BTNode* tree);

    int(*isLeaf)(BTNode* target);

    void* (*getNodeData)(BTNode* target);

    BTNode* (*getLeftChild)(BTNode* target);

    BTNode* (*getRightChild)(BTNode* target);

    void(*traverseTree)(BTNode* root, void(*visit)(BTNode* cur)); // 打印树root所有内容,中序遍历
    void(*traverseTreeWithStack)(BTNode* root, void(*visit)(BTNode* node));  //  打印以root为树根所有内容 ,中序遍历 inorder

    void(*traverseTreePreorder)(BTNode* root, void(*visit)(BTNode* node)); // 打印树root所有内容,前序遍历
    void(*traverseTreePreorderWithStack)(BTNode* root, void(*visit)(BTNode* node)); // 打印树root所有内容,前序遍历

    void(*traverseTreePostorder)(BTNode* root, void(*visit)(BTNode* node)); // 递归,后序遍历
    void(*traverseTreePostorderWithStack)(BTNode* root, void(*visit)(BTNode* node)); //  打印以root为树根所有内容 ,后序遍历 postorder

    void(*traverseWhich)(BTNode* root, void(*visit)(BTNode* node), int which) ;// 选一个遍历算法
} BTree;

2.1.2 声明相关操作函数

void BTreeInit(BTree* tree, void(*destory)(void* data)); // 对一个树进行初始化

void BTreeInitWithCompare(BTree* tree, void(*destory)(void* data), int(*compare)(const void* arg0, const void* arg1));

void BTreeDestory(BTree* tree);

BTree* createBTree(void(*destory)(void* data));  // 创建树

int insertRight(BTree*  tree, BTNode* target, const void* data); // 插入到目标的右边

int insert(struct BinaryTree*  tree, const void* data); // 比较值的大小来排序

int insertLeft(BTree*  tree, BTNode* target, const void* data); // 插入到目标的左边

int removeLeft(BTree*  tree, BTNode* target);  // 删除左子树

int removeRight(BTree*  tree, BTNode* target);  // 删除右子树

int getSize(BTree* tree);  //获取树大小

BTNode* getRootNode(BTree* tree);  // 树根节点

int isEndOfTree(BTNode* target);  // 是否到了树的尾端

int isLeaf(BTNode* target);  // 是否为叶子节点

void* getNodeData(BTNode* target); // 节点值

BTNode* getLeftChild(BTNode* target); // 右子节点

BTNode* getRightChild(BTNode* target); // 左子节点

void traverseTree(BTNode* root, void(*visit)(BTNode* cur)); // 打印树root所有内容,中序遍历
void traverseTreeWithStack(BTNode* root, void(*visit)(BTNode* node));  //  打印以root为树根所有内容 ,中序遍历 inorder

void traverseTreePreorder(BTNode* root, void(*visit)(BTNode* node)); // 打印树root所有内容,前序遍历
void traverseTreePreorderWithStack(BTNode* root, void(*visit)(BTNode* node)); // 打印树root所有内容,前序遍历

void traverseTreePostorder(BTNode* root, void(*visit)(BTNode* node)); // 递归,后序遍历
void traverseTreePostorderWithStack(BTNode* root, void(*visit)(BTNode* node)); //  打印以root为树根所有内容 ,后序遍历 postorder

void traverseWhich(BTNode* root, void(*visit)(BTNode* node),int which); // 选一个遍历算法

2.2 实现C文件

2.2.1 创建与销毁链表

// 初始化函数指针
initRefs(BTree* tree)
{
    // init
    tree->BTreeInit = BTreeInit;
    tree->BTreeInitWithCompare = BTreeInitWithCompare;
    tree->BTreeDestory = BTreeDestory;
    // modify
    tree->insertRight = insertRight;
    tree->insert = insert;
    tree->insertLeft = insertLeft;
    tree->removeLeft = removeLeft;
    tree->removeRight = removeRight;
    //query
    tree->getSize = getSize;
    tree->getRootNode = getRootNode;
    tree->isEndOfTree = isEndOfTree;
    tree->isLeaf = isLeaf;
    tree->getNodeData = getNodeData;
    tree->getLeftChild = getLeftChild;
    tree->getRightChild = getRightChild;
    /// traverse
    tree->traverseTree = traverseTree;
    tree->traverseTreeWithStack = traverseTreeWithStack;
    tree->traverseTreePostorder = traverseTreePostorder;
    tree->traverseTreePostorderWithStack = traverseTreePostorderWithStack;
    tree->traverseTreePreorder = traverseTreePreorder;
    tree->traverseTreePreorderWithStack = traverseTreePreorderWithStack;
    tree->traverseWhich = traverseWhich; 
}

// 对空树进行初始化
void BTreeInit(BTree* tree, void(*destory)(void* data))
{
    if (tree == NULL)
    {
        return ;
    }

    tree->size = 0;
    tree->destory = destory;
    tree->root = NULL;

    initRefs(tree);
}

// 提供比较器,用于插入时
void BTreeInitWithCompare(BTree* tree, int(*compare)(const void* arg0, const void* arg1))
{
    if (tree == NULL)
    {
        return;
    }
    tree->compare = compare;
}

// 创建树
BTree* createBTree(void(*destory)(void* data))
{
    BTree* tree = (BTree*)malloc(sizeof(BTree));
    if (tree == NULL)
    {
        return NULL;
    }

    BTreeInit(tree, destory);

    return tree;
}

// 创建树,元素可比较大小,提供比较器
BTree* createComparableBTree(void(*destory)(void* data), int(*compare)(const void* arg0, const void* arg1))
{
    BTree* tree = (BTree*)malloc(sizeof(BTree));
    if (tree == NULL)
    {
        return NULL;
    }

    BTreeInit(tree, destory);
    BTreeInitWithCompare(tree,compare);

    return tree;
}

void BTreeDestory(BTree* tree)
{
    if (tree == NULL)
    {
        return;
    }

    removeLeft(tree, NULL); 

    memset(tree, 0, sizeof(BTree)); // 将内存重置为0
}

2.2.2 增删节点

// 插入到目标节点左边
int insertLeft(BTree*  tree, BTNode* target, const void* data) 
{
    if (tree == NULL || data == NULL)
    {
        return -1;
    }

    BTNode *newNode, **position = NULL;
    if (target == NULL)  // 添加为root
    {
        if (tree->root != NULL) return -1;// 不重复添加

        position = &tree->root;
    }
    else{
        if (target->right != NULL) return -1;

        position = &target->left;
    }

    if ((newNode = malloc(sizeof(BTNode))) == NULL)
    {
        return -1;
    }

    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    *position = newNode;

    tree->size++;
    return 1;
}

// 插入到目标节点右边
int insertRight(BTree*  tree, BTNode* target, const void* data)
{
    if (tree == NULL || data == NULL)
    {
        return -1;
    }

    BTNode *newNode, **position = NULL;
    if (target == NULL)  // 添加为root
    {
        if (tree->root != NULL) return -1;// 不重复添加

        position = &tree->root;
    }
    else{
        if (target->right != NULL) return -1;

        position = &target->right;
    }

    if ((newNode = malloc(sizeof(BTNode))) == NULL)
    {
        return -1;
    }
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    *position = newNode;

    tree->size++;
    return 1;
}

int insert(struct BinaryTree*  tree, const void* data) // 比较值的大小来排序
{
    if (tree == NULL || tree->compare == NULL)
    {
        return -1;
    }

    BTNode* newNode = NULL;
    if ((newNode = (BTNode*)malloc(sizeof(BTNode))) == NULL)
    {
        return -1;
    }

    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;

    BTNode* current = tree->root;
    if (current == NULL)  // 根为NULL
    {
        tree->root = newNode;
        tree->size++; 
        return 1;
    }

    while (1)
    {
        if (tree->compare(data,current->data) >= 0) // 大于则在右子树
        {
            if (current->right == NULL)
            {
                current->right = newNode; // 添加到对应位置
                break;
            }
            current = current->right;
        }
        else{   // 否则左子树
            if (current->left == NULL)  // 插入到当前的左child
            {
                current->left = newNode;
                break;
            }

            current = current->left;
        }
    }

    tree->size++;

    return 1;
}

 // 删除左子树
int removeLeft(BTree*  tree, BTNode* target) 
{
    if (tree == NULL || tree->size == 0)
    {
        return -1;
    }

    BTNode **position; // 需要释放的子树位置
    if (target == NULL)  //target 为NULL,则开始删除的位置为root
    {
        position = &tree->root;
    }
    else
    {
        position = &target->left;
    }

    if (*position == NULL)
    {
        return 0;
    }

    removeLeft(tree, *position);
    removeRight(tree, *position);

    if (tree->destory != NULL)
    {
        tree->destory((*position)->data);
    }

    free(*position);
    *position = NULL;

    tree->size--;

    return 1;
}

// 删除右子树
int removeRight(BTree*  tree, BTNode* target)  
{
    if (tree == NULL || tree->size == 0)
    {
        return -1;
    }

    BTNode** position; // 需要释放的子树位置
    if (target == NULL)
    {
        position = &tree->root;
    }
    else
    {
        position = &target->right;
    }

    if (*position == NULL)
    {
        return 0;
    }

    removeLeft(tree, *position);
    removeRight(tree, *position);

    if (tree->destory != NULL)
    {
        tree->destory((*position)->data);
    }

    free(*position);
    *position = NULL;

    tree->size--;

    return 1;
}

2.2.3 查找与获取

// 获取树的size
int getSize(BTree* tree)
{
    if (tree == NULL)
    {
        return -1;
    }
    return tree->size;
}

// 获取根节点
BTNode* getRootNode(BTree* tree)
{
    if (tree == NULL)
    {
        return NULL;
    }
    return tree->root;
}

// 是否到了末尾
int isEndOfTree(BTNode* target)
{
    return target == NULL;
}

// 是否为叶子节点
int isLeaf(BTNode* target)
{
    if (target == NULL) // 目标节点不存在
    {
        return -1;
    }

    return target->left == NULL && target->right == NULL ? 1 : 0;
}

// 获取目标的value值
void* getNodeData(BTNode* target)
{
    if (target == NULL)
    {
        return NULL;
    }
    return target->data;
}

// 获取目标的左child
BTNode* getLeftChild(BTNode* target)
{
    if (target == NULL)
    {
        return NULL;
    }

    return target->left;
}

// 获取目标的右child
BTNode* getRightChild(BTNode* target)
{
    if (target == NULL)
    {
        return NULL;
    }

    return target->right;
}

2.2.3 二叉树的遍历

递归形式
// 递归,打印树root所有内容,前序遍历
void traverseTreePreorder(BTNode* root, void(*visit)(BTNode* node)) 
{
    if (root == NULL || visit == NULL)
    {
        return;
    }

    visit(root);
    traverseTreePreorder(root->left, visit);
    traverseTreePreorder(root->right, visit);

}

// 递归,打印树root所有内容,中序遍历
void traverseTree(BTNode* root, void(*visit)(BTNode* node)) 
{
    if (root == NULL || visit == NULL)
    {
        return;
    }

    traverseTree(root->left,visit);
    visit(root);
    traverseTree(root->right,visit);

}

// 递归,打印树root所有内容,后序遍历
void traverseTreePostorder(BTNode* root, void(*visit)(BTNode* node)) 
{
    if (root == NULL || visit == NULL) return;

    traverseTreePostorder(root->left,visit);
    traverseTreePostorder(root->right, visit);
    visit(root);
}
非递归形式(使用辅助栈)
  • 前序遍历:
// 辅助函数:一路访问left节点,并将相应的right节点入栈
void visitAlongWithLeft(BTNode* root, Stack* stack, void(*visit)(BTNode* node))
{
    if (root == NULL || visit == NULL) return;

    while (root != NULL)
    {
        visit(root); // 访问当前

        if (root->right != NULL)
        {
            // right入栈
            StackElem* elem = malloc(sizeof(StackElem));
            elem->value = root->right;
            pushStack(stack, elem);
        }

        // 下一个left
        root = root->left;
    }
}

// 使用栈,前序遍历
void traverseTreePreorderWithStack(BTNode* root, void(*visit)(BTNode* node)) // 打印以root为树根所有内容,前序遍历 preorder
{
    if (root == NULL || visit == NULL){
        return;
    }

    Stack* stack = initStack();
    BTNode* current = root;
    if (stack == NULL) return;

    while (1)
    {
        visitAlongWithLeft(current, stack, visit);
        if (isStackEmpty(stack)) break;

        StackElem* elem = popStack(stack);
        current = (BTNode*)elem->value; // 取出右子树,继续执行上面过程

        free(elem);  // 释放栈元素内存
    }

    free(stack); // 释放栈内存
}
  • 中序遍历:

// 辅助函数:从root开始,一直将所有left入栈
void goAloneWithLeft(BTNode* root, Stack* stack)
{
    if (root == NULL) return;

    while (root != NULL)
    {
        StackElem* elem = malloc(sizeof(StackElem));
        elem->value = root;
        pushStack(stack, elem);
        root = root->left;
    }
}

 //  使用辅助栈,打印以root为树根所有内容 ,中序遍历 inorder
void traverseTreeWithStack(BTNode* root, void(*visit)(BTNode* node)) 
{
    if (root == NULL || visit == NULL) {
        return;
    }

    Stack* stack = initStack();
    BTNode* current = root;
    if (stack == NULL) return;

    while (1)
    {
        goAloneWithLeft(current, stack);

        if (isStackEmpty(stack)) break;

        StackElem* elem = popStack(stack);

        BTNode* leftEdge = (BTNode*)elem->value; // 左边最后一个未访问的
        visit(leftEdge);
        current = leftEdge->right; // 遍历右子树,重复上面过程

        free(elem);  // 释放栈元素内存
    }

    free(stack); // 释放栈内存
}
  • 后序遍历:

//  打印以root为树根所有内容 ,后序遍历 postorder  非递归
void traverseTreePostorderWithStack(BTNode* root, void(*visit)(BTNode* node)) 
{
    if (root == NULL || visit == NULL)
    {
        return;
    }

    Stack* stack = initStack();
    if (stack == NULL)
    {
        return;
    }

    BTNode* current = root;

    while (1)
    {
        goAloneWithLeft(current, stack);

        if (isStackEmpty(stack)) break;

        StackElem* elem = peakStack(stack);

        BTNode* leftEdge = (BTNode*)elem->value; // 左边最后一个

        if (leftEdge->right != NULL && elem->peak <= 1) // 右元素还没有遍历
        {       
            current = leftEdge->right; // 遍历右子树,重复上面过程
        }
        else
        {
            popStack(stack); // 出栈
            visit(leftEdge);
            current = NULL; // 置NULL,之后从stack取上一个left
            free(elem);  // 释放栈元素内存
        }

    }

    free(stack); // 释放栈内存
}

3、实现辅助栈

3.1 栈头文件

#pragma once
#include<stdlib.h>
#include<stdio.h>

// 栈存储元素
typedef struct StackElem_
{
    void* value;
    struct StackElem_* next;
    struct StackElem_* prev;
    int peak;  // peak的次数
}StackElem;

// 栈结构
typedef struct Stack_
{
    int size; 
    StackElem* top;
} Stack;

// 初始化空栈
Stack* initStack();

// 压栈
void pushStack(Stack* stack, StackElem* elem);

// 弹出栈
StackElem* popStack(Stack* stack);

// 获取栈顶值而不弹出
StackElem* peakStack(Stack* stack);


void showAll(Stack* stack);

// 是否为空栈
int isStackEmpty(Stack* stack);

3.2 栈实现

#include"link_stack.h"


Stack* initStack()
{
    Stack* stack = malloc(sizeof(Stack));
    stack->size = 0;
    stack->top = NULL; 

    return stack;
}

// 压栈
void pushStack(Stack* stack, StackElem* elem)
{
    if (stack == NULL || elem == NULL)
    {
        return;
    }

    elem->peak = 0;
    if (stack->top == NULL)
    {
        stack->top = elem;
        elem->prev = NULL;
        elem->next = NULL;
    }
    else
    {
        stack->top->next = elem;
        elem->prev = stack->top;
        elem->next = NULL;
        stack->top = elem;  // top后移
    }

    stack->size++;
    //printf("\nsize=%d\n", stack->size);
}

// 弹出栈
StackElem* popStack(Stack* stack)
{
    if (stack->size==0)
    {
        return NULL;
    }

    StackElem* elem = stack->top;
    stack->top = stack->top->prev;
    if (stack->top != NULL)
    {
        stack->top->next;
    }
    stack->size--; 
    return elem;
}

// 获取栈顶的值,而弹出栈
StackElem* peakStack(Stack* stack)
{
    if (stack->size == 0)
    {
        return NULL;
    }
    stack->top->peak += 1;
    return stack->top;
}


void showAll(Stack* stack)
{
    if (stack != NULL)
    {
        StackElem* current =  stack->top;
        while (current != NULL)
        {
            printf("current is %d\n", *(int*)(current->value));
            current = current->prev;
        }
    }
}

// 返回非0为空
int isStackEmpty(Stack* stack)
{
    return stack->size == 0;
}

4、使用

#include"BinaryTree.h"
#include"Log.h"
#include"link_stack.h"

// 比较函数
int compare(int* pa, int* pb)
{
    int a = *pa;
    int b = *pb;
    return (a > b) ? 1 : ((a == b) ? 0 : -1);
}

// 打印
void printNode(BTNode* node)
{
    if (node == NULL || node->data == NULL)
    {
        return;
    }
    printf("current data is %d\n", *(int*)(node->data));
}

void main()
{
    BTree* tree = createComparableBTree(NULL,compare);
    printf("%#x\n", &tree->root);
    int a = 1, b = 2, c = 3,d=-1,e=-2,f=0,h=6,k=-6;
    tree->insert(tree, &a);
    tree->insert(tree, &d);
    tree->insert(tree, &c);
    tree->insert(tree, &b);
    tree->insert(tree, &e);
    tree->insert(tree, &f);
    tree->insert(tree, &h);
    tree->insert(tree, &k);


    //printf("%d\n", *(int*)(tree->root->data));

    //tree->traverseTree(tree->root,printNode);
    //tree->traverseTreeWithStack(tree->root, printNode);

    //tree->traverseTreePreorder(tree->root,printNode);
    //tree->traverseTreePreorderWithStack(tree->root, printNode);

    //tree->traverseTreePostorder(tree->root, printNode);
    tree->traverseTreePostorderWithStack(tree->root, printNode);
    system("pause");
}

测试traverseTreePostorderWithStack输出:

current data is -6
current data is -2
current data is 0
current data is -1
current data is 2
current data is 6
current data is 3
current data is 1

源码

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值