数据结构| |二叉树的基本操作

该博客直接将代码都粘贴上来,有测试函数,所以大家可以下载测试一下,也可学习接口的实现以及递归思想,我已将所有接口实现的思想也写在代码中。
对于本篇代码中包含了Queue.h以及Stack.h的头文件,大家可以参考以前的博客,栈和队列的那一篇。
链接地址:https://blog.csdn.net/qq_40399012/article/details/81665085
直接上代码:

头文件:

#ifndef __BINARYTREE_H__
#define __BINARYTREE_H__


#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//引入队列是为了层次遍历该二叉树以及判断该二叉树是不是完全二叉树(层次遍历的变形)
//再看接口实现的时候,就可以看到判断二叉树是用层次遍历的思想来写的
#include "Queue.h"
//引入栈是为了非递归遍历该二叉树
#include "Stack.h"

typedef char BTDataType;

typedef struct BTNode
{
    BTDataType _data;
    struct BTNode* _left;
    struct BTNode* _right;
}BTNode;

//前序遍历创建一个二叉树
BTNode* BinaryTreeCreat(BTDataType* arr, int* pcur);
void BinaryTreeDestory(BTNode** root);

//遍历二叉树 递归&&非递归

//递归遍历
void BinaryTreePrevOrder(BTNode* root);
void BinaryTreeInOrder(BTNode* root);
void BinaryTreePostOrder(BTNode* root);

//层次遍历
void BinaryTreeLevelOrder(BTNode* root);

//非递归遍历
void BinaryTreePrevOrderNonR(BTNode* root);
void BinaryTreeInOrderNonR(BTNode* root);
void BinaryTreePostOrderNonR(BTNode* root);

//求二叉树的结点数
int BinaryTreeSize(BTNode* root);
//求二叉树的叶子结点的个数
int BinaryTreeLeafSize(BTNode* root);
//求二叉树的第K层的节点的个数
int BinaryTreeLevelKSize(BTNode* root, int k);
//求二叉树的高度
int BinaryTreeHeight(BTNode* root);
//判断一个结点是否在一个二叉树中
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
//求一个结点的双亲结点
BTNode* BinaryTreeFindParentsNode(BTNode* root, BTNode* aim_node);
//判断一个二叉树是不是完全二叉树
int BinaryTreeIsWhole(BTNode* root);
int BinaryTreeIsWhole_T(BTNode* root);
//求二叉树的镜像
//递归的方式
void BinaryTreeMirrorImage(BTNode* root);
//非递归的方式
void BinaryTreeMirrorImageNonR(BTNode* tree);

#endif //__BINARYTREE_H__

接口实现:

#include "BinaryTree_M.h"

//申请一个BTNode*类型的结点空间
BTNode* BuyBTNode(BTDataType x)
{
    BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
    if (newNode == NULL)
    {
        perror("In order get size!");
    }

    newNode->_data = x;
    newNode->_left = NULL;
    newNode->_right = NULL;
    return newNode;
}

//采用前序遍历创建一个二叉树
BTNode* BinaryTreeCreat(BTDataType* arr, int* pcur)
{
    assert(arr);

    if (arr[*pcur] == '#')
    {
        return NULL;
    }

    BTNode* root = BuyBTNode(arr[*pcur]);

    (*pcur)++;
    root->_left = BinaryTreeCreat(arr, pcur);
    (*pcur)++;
    root->_right = BinaryTreeCreat(arr, pcur);
    return root;
}

//二叉树的销毁函数,
//思想:对于二叉树进行销毁的话,必须从叶子节点开始进行销毁,否则就找不到其后面的结点了
void BinaryTreeDestory(BTNode** root)
{
    if (*root == NULL)
    {
        return;
    }

    //销毁的话就要全都销毁,只有当root结点的左右指针都为NULL的时候才销毁root结点
    //只有当左子树为空的时候,才能判断右子树,所以销毁放在两个遍历的后面
    BinaryTreeDestory(&(*root)->_left);
    BinaryTreeDestory(&(*root)->_right);
    if (((*root)->_left == NULL) && ((*root)->_right == NULL))
    {
        free((*root));
        (*root) = NULL;
    }
    //printf("销毁成功!\n");
}

//遍历二叉树 递归&&非递归
//前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
    if (root == NULL)
    {
        return;
    }
    printf("%c ", root->_data);
    BinaryTreePrevOrder(root->_left);
    BinaryTreePrevOrder(root->_right);
}

//中序遍历
void BinaryTreeInOrder(BTNode* root)
{
    if (root == NULL)
    {
        return;
    }
    BinaryTreeInOrder(root->_left);
    printf("%c ", root->_data);
    BinaryTreeInOrder(root->_right);
}

//后序遍历
void BinaryTreePostOrder(BTNode* root)
{
    if (root == NULL)
    {
        return;
    }

    BinaryTreePostOrder(root->_left);
    BinaryTreePostOrder(root->_right);
    printf("%c ", root->_data);
}

//层次遍历
//思想:对二叉树进行层次遍历采用队列的结构,队列具有先进先出的特点,结束:队列为空,则所有的元素都已经输出
void BinaryTreeLevelOrder(BTNode* root)
{
    Queue q;
    QueueInit(&q);

    if (root == NULL)
    {
        return;
    }
    QueuePush(&q, root);
    while (QueueEmpty(&q))
    {
        BTNode* ret = QueueFront(&q);
        printf("%c ", ret->_data);
        QueuePop(&q);
        if (ret->_left)
        {
            QueuePush(&q, ret->_left);
        }
        if (ret->_right)
        {
            QueuePush(&q, ret->_right);
        }
    }
}

//非递归遍历二叉树
//前序遍历(根节点  左子树    右子树)
//思路:先将该二叉树的左路结点压栈遍历完,然后在访问根节点,然后再对右结点进行刚才同样的事情,
//进行左路结点压栈遍历,然后在访问右结点,结束条件:该节点是叶子就不需要在进行压栈,对其进行出栈就可以了
void BinaryTreePrevOrderNonR(BTNode* root)
{
    assert(root);

    Stack s;
    StackInit(&s);
    BTNode* cur = root;

    //StackEmpty必须存在因为在遍历的过程中,cur有可能会等于NULL
    while (cur || StackEmpty(&s))
    {
        //将左路结点进行压栈
        while (cur)
        {
            StackPush(&s, cur);
            printf("%c ", cur->_data);
            cur = cur->_left;
        }

        BTNode* top = StackTop(&s);
        StackPop(&s);

        cur = top->_right;
    }

    printf("\n");
}

//中序遍历(左子树  根节点   右子树)
//思想:中序遍历和前序遍历基本一样,只是在输出结点数据的位置不同,中序遍历是在对该节点进行出栈的时候,访问该节点
void BinaryTreeInOrderNonR(BTNode* root)
{
    assert(root);

    Stack s;
    StackInit(&s);
    BTNode* cur = root;

    //StackEmpty必须存在因为在遍历的过程中,cur有可能会等于NULL
    while (cur || StackEmpty(&s))
    {
        //将左路结点进行压栈
        while (cur)
        {
            StackPush(&s, cur);
            cur = cur->_left;
        }

        BTNode* top = StackTop(&s);
        printf("%c ", top->_data);
        StackPop(&s);

        cur = top->_right;
    }

    printf("\n");
}

//后序遍历(左子树   右子树   根节点)
//思想:先将左路结点全部入栈,然后出栈的时候
//如何判断是否要访问该结点,
//如果该节点的右结点为空的话,或者该结点的右结点等于刚访问的上一个节点prev,那就直接访问该节点
//否则就是该节点的右结点没有进行访问呢,那就访问该节点的右结点
void BinaryTreePostOrderNonR(BTNode* root)
{
    assert(root);

    Stack s;
    StackInit(&s);
    //prev记录的是出栈的结点
    BTNode* prev = NULL;
    BTNode* cur = root;

    //cur必须存在因为开始要从cur进入,只不过是在遍历的时候,cur绝对不会等于NULL
    while (cur || StackEmpty(&s))
    {
        //将左路结点全部入栈
        while (cur)
        {
            StackPush(&s, cur);
            cur = cur->_left;
        }

        BTNode* top = StackTop(&s);
        //说明该节点的右结点已经访问过了,可以进行输出了
        if (top->_right == NULL || top->_right == prev)
        {
            StackPop(&s);
            prev = top;
            printf("%c ", top->_data);
        }
        //该节点的右结点没有访问过,那就继续对右结点进行子循环
        else
        {
            cur = top->_right;
        }
    }
    printf("\n");
}

//求二叉树的结点数
//二叉树的结点数等于左子树的结点数和右子树的结点数之和再加上根节点, 结束:root == NULL
int BinaryTreeSize(BTNode* root)
{
    if (root == NULL)
    {
        return 0;
    }
    return 1 + BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right);
}

//求二叉树的叶子结点的个数
//思想:求根节点的左子树的叶子节点的个数加上右子树的叶子节点的个数,在进行返回,
//结束:root->_left == NULL,root->_right == NULL, return 1;  root == NULL , return 0;
int BinaryTreeLeafSize(BTNode* root)
{
    if (root == NULL)
    {
        return 0;
    }
    if ((root->_left == NULL) && (root->_right == NULL))
    {
        return 1;
    }
    return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}

//求二叉树的第K层的节点的个数
//思想:设根节点是第一层,则方法是求第K层,就是求该节点的左子树的K-1层的个数加上右子树K-1层的个数,结束:K == 1
int BinaryTreeLevelKSize(BTNode* root, int k)
{
    if (root == NULL)
    {
        return 0;
    }
    if (k == 1)
    {
        return 1;
    }
    return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}

//对于在递归中要返回结点必须要在该递归函数中,进行局部变量的定义,使用局部变量进行返回与接收。
//如果不用局部变量进行接收的话,那么就会导致返回的东西丢失

//求二叉树的高度
//思想:根节点的高度等于左右子树大的高度加上1,结束:root == NULL
int BinaryTreeHeight(BTNode* root)
{
    if (root == NULL)
    {
        return 0;
    }
    int leftHeight = BinaryTreeHeight(root->_left);
    int rightHeight = BinaryTreeHeight(root->_right);
    return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

//判断一个结点是否在一个二叉树中
//思想:先检测根节点是否相等,在检测左子树,在检测右子树,结束:root->_data == x, root == NULL
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
    if (root == NULL)
    {
        return NULL;
    }
    if (root->_data == x)
    {
        return root;
    }
    BTNode* ret = BinaryTreeFind(root->_left, x);
    if (ret)
    {
        return ret;
    }
    ret = BinaryTreeFind(root->_right, x);
    if (ret)
    {
        return ret;
    }
    return NULL;
}

//求一个结点的双亲结点
//思路:从根节点开始,判断根节点的左子树或者右子树等不等于所求结点,
//如果是的话,就返回该节点,否则在判断该节点的左子树和右子树
//结束:root == NULL
BTNode* BinaryTreeFindParentsNode(BTNode* root, BTNode* aim_node)
{
    if (root == NULL)
    {
        return NULL;
    }
    if ((root->_left == aim_node) || (root->_right == aim_node))
    {
        return root;
    }
    BTNode* ret = BinaryTreeFindParentsNode(root->_left, aim_node);
    if (ret)
    {
        return ret;
    }
    ret = BinaryTreeFindParentsNode(root->_right, aim_node);
    if (ret)
    {
        return ret;
    }
    return NULL;
}

//检测队列里面是否包含数据元素
//思路:遍历一遍队列,如果
//有返回1,没有0
int CheckHaveData(Queue* pq)
{
    assert(pq);

    //对队列进行一次遍历只要含有数据元素就进行返回
    while (pq->front)
    {
        if (pq->front->data->_data != '#')
        {
            return 1;
        }
        pq->front = pq->front->next;
    }
    return 0;
}

//判断一个二叉树是不是完全二叉树
//思路:可以采用层次遍历,如果某一个结点是空的话也进行入队,变成入队#,最后只要队列里出现,#后面仍然有数据元素,
//那么该队列就不是完全二叉树,否则就是完全二叉树
//是完全二叉树 1
//不是完全二叉树 0
int BinaryTreeIsWhole(BTNode* root)
{
    Queue q;
    QueueInit(&q);
    BTNode* tmp_null = BuyBTNode('#');
    //判断根节点是不是空
    if (root == NULL)
    {
        return 0;
    }
    QueuePush(&q, root);
    while (QueueEmpty(&q))
    {
        BTNode* ret = QueueFront(&q);
        QueuePop(&q);
        //每次都要检测一下,看现在队列里面是不是含数据元素
        if (ret->_data == '#')
        {
            if (CheckHaveData(&q))
            {
                return 0;
            }
            return 1;
        }
        if (ret->_left == NULL)
        {
            QueuePush(&q, tmp_null);
        }
        else
        {
            QueuePush(&q, ret->_left);
        }
        if (ret->_right == NULL)
        {
            QueuePush(&q, tmp_null);
        }
        else
        {
            QueuePush(&q, ret->_right);
        }
    }
    return 1;
}

int BinaryTreeIsWhole_T(BTNode* root)
{
    Queue q;
    QueueInit(&q);
    QueuePush(&q, root);

    if (root)
    {
        while (QueueEmpty(&q))
        {
            BTNode* front = QueueFront(&q);
            QueuePop(&q);
            if (front == NULL)
            {
                break;
            }
            else
            {
                QueuePush(&q, front->_left);
                QueuePush(&q, front->_right);
            }
        }
    }
    while (QueueEmpty(&q))
    {
        BTNode* front = QueueFront(&q);
        if (front == NULL)
        {
            QueuePop(&q);
        }
        else
        {
            //不是完全二叉树
            QueueDestory(&q);
            return 0;
        }
    }
    //是完全二叉树
    return 1;
}

//交换左右子树
void SwapLeftAndRight(BTNode** left, BTNode** right)
{
    assert(left && right);

    BTNode* tmp = (*left);
    (*left) = (*right);
    (*right) = tmp;
}

//求二叉树的镜像
//交换左右子树,采用递归的方法
//交换根节点的左右子树,就交换左子树的左右子树,交换右子树的左右子树
void BinaryTreeMirrorImage(BTNode* root)
{
    //空的二叉树就是镜像与原来的都一样,无需在进行求镜像
    if (root == NULL)
    {
        return;
    }

    SwapLeftAndRight(&root->_left, &root->_right);
    BinaryTreeMirrorImage(root->_left);
    BinaryTreeMirrorImage(root->_right);
}

//求二叉树的镜像
//采用非递归的方式
//思路:就和非递归的二叉树的一样,对于根节点是最后进行二叉树的交换的,与递归的方向刚好相反
void BinaryTreeMirrorImageNonR(BTNode* tree)
{
    //空的二叉树就是镜像与原来的都一样,无需在进行求镜像
    if (tree == NULL)
    {
        return;
    }

    Stack s;
    StackInit(&s);
    BTNode* cur = tree;
    BTNode* prev = NULL;

    while (cur || StackEmpty(&s))
    {
        //将左路结点全部入栈
        while (cur)
        {
            StackPush(&s, cur);
            cur = cur->_left;
        }

        BTNode* top = StackTop(&s);
        if (top->_right == NULL || top->_right == prev)
        {
            SwapLeftAndRight(&top->_left, &top->_right);
            prev = top;
            StackPop(&s);
        }
        else
        {
            cur = top->_right;
        }
    }
}

测试函数;

#include "BinaryTree_M.h"

void TestBinaryTree()
{
    int cur = 0;
    char arr[] = { 'A', 'B', 'C', '#', '#', 'D', '#', '#','E', 'F', '#', '#', 'G', '#', '#' };
    BTNode* tree = BinaryTreeCreat(arr, &cur);

    printf("先序遍历:\n");
    BinaryTreePrevOrder(tree);
    printf("\n");
    BinaryTreePrevOrderNonR(tree);

    printf("中序遍历:\n");
    BinaryTreeInOrder(tree);
    printf("\n");
    BinaryTreeInOrderNonR(tree);

    printf("后序遍历:\n");
    BinaryTreePostOrder(tree);
    printf("\n");
    BinaryTreePostOrderNonR(tree);

    printf("层次遍历:\n");
    BinaryTreeLevelOrder(tree);
    printf("\n");
    printf("二叉树的结点个数:%d\n", BinaryTreeSize(tree));
    printf("二叉树的叶子节点个数:%d\n", BinaryTreeLeafSize(tree));
    printf("二叉树第3层结点的个数:%d\n", BinaryTreeLevelKSize(tree, 3));
    printf("二叉树节点的高度:%d\n", BinaryTreeHeight(tree));

    //测试查找一个结点
    BTNode* ret = BinaryTreeFind(tree, 'C');
    if (ret)
    {
        printf("查找%c数据,找到\n", ret->_data);
    }
    else
    {
        printf("查找%c数据,没有找到!\n", ret->_data);
    }

    //测试查找一个结点的双亲结点
    BTNode* parents = BinaryTreeFindParentsNode(tree, ret);
    if (parents)
    {
        printf("找到%c结点的双亲结点为:%c\n", ret->_data, parents->_data);
    }
    else
    {
        printf("没有找到!\n");
    }

    //if (BinaryTreeIsWhole(tree))
    if (BinaryTreeIsWhole_T(tree))
    {
        printf("该二叉树是完全二叉树!\n");
    }
    else
    {
        printf("该二叉树不是完全的二叉树!\n");
    }

    //二叉树的镜像
    printf("二叉树的镜像:\n");
    BinaryTreeMirrorImage(tree);
    BinaryTreePrevOrder(tree);
    printf("\n");

    printf("二叉树的镜像:\n");
    BinaryTreeMirrorImageNonR(tree);
    BinaryTreePrevOrder(tree);
    printf("\n");

    BinaryTreeDestory(&tree);
}


int main()
{
    TestBinaryTree();
    return 0;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值