【数据结构C/C++】顺序与链式二叉树创建与前中后、层序遍历

news2024/11/19 18:40:02

文章目录

  • 顺序存储结构二叉树
  • 链式存储结构二叉树
  • 刷题推荐
  • 408考研各数据结构C/C++代码(Continually updating)

顺序存储结构二叉树

顺序存储结构的二叉树的特点在于,其使用数组存放二叉树中的每一个节点。
我们设定根节点的数组索引下标为n(n>=1),那么有当前根节点的左子树节点在数组中的下标为2n,右子树在数组中的下标为2n+1。
上面是顺序存储结构二叉树的最最最重要的一个特性,我们后面的所有操作都基于这个理论。
相对于比较简单的对于链式存储结构的二叉树的遍历,以及计算最大二叉树深度,链式存储结构都会更加接单,而顺序存储结构则需要考虑到当前节点是否为空,并且当前索引是否超出了树当前最大的节点个数。

LeetCode计算二叉树最大深度
在这里插入图片描述

这里我不推荐你花大把时间去实现一个顺序存储结构的二叉树的层序遍历,因为我们知道,层序遍历一般的解法都是配合一个队列或者栈来实现的。
我在写Java的时候一般都会使用Deque也就是双端队列来实现,但是C语言并没有提供这样子的功能,你还得手动实现,比较费时,因此我不太推荐。
当然,有兴趣的可以去刷一下LeetCode链式存储结构的层序遍历,还是比较常见的。

先附上一张代码运行的图片。
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>

#define MaxSize 100

typedef char TreeNodeType;

//二叉树结构
typedef struct
{
    TreeNodeType data[MaxSize];
    int BiTreeNum;
} BinaryTree;

// 声明队列数据结构
typedef struct
{
    int front, rear;
    int size;
    int capacity;
    int *array;
} Queue;

void initTree(BinaryTree *T);
void createTree(BinaryTree *T, int n);
TreeNodeType rootTree(BinaryTree *T);
int countTree(BinaryTree *T);
int maxDepthOfTree(BinaryTree *T, int n);
void preOrderTraverse(BinaryTree *T, int n);
void inOrderTraverse(BinaryTree *T, int n);
void postOrderTraverse(BinaryTree *T, int n);
void levelOrderTraverse(BinaryTree *T); // 层序遍历
bool destoryTree(BinaryTree *T);
void traverseArray(BinaryTree *T); // 遍历数组


// 队列相关函数
Queue *createQueue(int capacity);
bool isQueueEmpty(Queue *queue);
bool isQueueFull(Queue *queue);
void enqueue(Queue *queue, int item);
int dequeue(Queue *queue);


int main()
{
    BinaryTree T;
    // 开始构造二叉树 其中使用1作为根节点下标
    // 而不是使用0,使用0的问题在于不好表示数据在数组中的位置
    // 我们知道二叉树满足 当前节点n的左子树和右子树的序列号应该为 2n和2n+1
    initTree(&T);
    printf("请输入根结点(输入#表示该结点为空):");
    createTree(&T, 1);

    traverseArray(&T);

    printf("当前二叉树的最大深度为:%d\n", maxDepthOfTree(&T, 1));

    printf("先序遍历:");
    preOrderTraverse(&T, 1);
    printf("\n");

    printf("中序遍历:");
    inOrderTraverse(&T, 1);
    printf("\n");

    printf("后序遍历:");
    postOrderTraverse(&T, 1);
    printf("\n");

    printf("层序遍历:");
    levelOrderTraverse(&T);
    printf("\n");

    return 0;
}


void initTree(BinaryTree *T)
{
    int i;
    for (i = 0; i < MaxSize; ++i)
    {
        T->data[i] = '\0';
    }

    T->BiTreeNum = 0;
    return;
}

void createTree(BinaryTree *T, int n)
{
    char ch;
    // 刷新(清空)标准输入流(stdin)
    // 主打一个求稳
    fflush(stdin);
    // 输入当前节点信息 # 代表当前节点为空
    // 先构造过字数
    scanf(" %c", &ch);
    if (ch == '#')
    { // 空直接返回
        return;
    }
    else
    {
        T->data[n] = ch;
        T->BiTreeNum++;
        printf("输入 %c 的左子树数据(输入#表示当前左子树为空: ", ch);
        // 这里有一个技巧
        createTree(T, 2 * n);
        printf("输入 %c 的右子树数据(输入#表示当前右边子树为空): ", ch);
        createTree(T, (2 * n + 1));
    }
}


// 计算二叉树的最大深度
// 从根节点到叶子节点的最长路径的长度
// 由于是顺序结构 因此这里从第一层也就是n=1开始向下遍历
// 然后不断的判断左子树和右子树的高度
// 最后取最大值
int maxDepthOfTree(BinaryTree *T, int n)
{
    if (n <= T->BiTreeNum && T->data[n] != '\0')
    {
        int leftDepth = maxDepthOfTree(T, 2 * n);
        int rightDepth = maxDepthOfTree(T, 2 * n + 1);

        return 1 + fmax(leftDepth, rightDepth);
    }
    else
    {
        return 0;
    }
}

//先序遍历 根左右
void preOrderTraverse(BinaryTree *T, int n)
{
    if (T->data[n] == '\0')
        return;
    else
    {
        printf("%c ", T->data[n]);
        preOrderTraverse(T, 2 * n);
        preOrderTraverse(T, (2 * n + 1));
    }
}
//中序遍历 左根由7
void inOrderTraverse(BinaryTree *T, int n)
{
    if (T->data[n] == '\0')
        return;
    else
    {
        inOrderTraverse(T, 2 * n);
        printf("%c ", T->data[n]);
        inOrderTraverse(T, (2 * n + 1));
    }
}
//后序遍历  左右根
void postOrderTraverse(BinaryTree *T, int n)
{
    if (T->data[n] == '\0')
        return;
    else
    {
        postOrderTraverse(T, 2 * n);
        postOrderTraverse(T, (2 * n + 1));
        printf("%c ", T->data[n]);
    }
}
void traverseArray(BinaryTree *T){
    for(int i=1;i<=T->BiTreeNum;i++){
        printf("%c  ",T->data[i]);
    }
    printf("\n");
}

// 层序遍历二叉树
void levelOrderTraverse(BinaryTree *T)
{
    if (T->BiTreeNum == 0)
    {
        printf("二叉树为空\n");
        return;
    }
	//创建一个队列存放当前层
    Queue *queue = createQueue(T->BiTreeNum);
    int current = 1; // 从根节点开始
	//结束条件
    while (current <= T->BiTreeNum)
    {
        printf("%c ", T->data[current]);
		//判断左子树是否存在 存在就放入队列
        if (2 * current <= T->BiTreeNum && T->data[2 * current] != '\0')
        {
            enqueue(queue, 2 * current);
        }
		//判断右子树是否存在 存在就放入队列
        if (2 * current + 1 <= T->BiTreeNum && T->data[2 * current + 1] != '\0')
        {
            enqueue(queue, 2 * current + 1);
        }
		//出队对头 
        if (!isQueueEmpty(queue))
        {
            current = dequeue(queue);
        }
        else
        {
            break;
        }
    }
	//使用完毕释放空间
    free(queue->array);
    free(queue);
}

// 创建队列
Queue *createQueue(int capacity)
{
    Queue *queue = (Queue *)malloc(sizeof(Queue));
    if (!queue)
    {
        perror("内存分配失败");
        exit(EXIT_FAILURE);
    }

    queue->front = 0;
    queue->rear = -1;
    queue->size = 0;
    queue->capacity = capacity;
    queue->array = (int *)malloc(capacity * sizeof(int));
    if (!queue->array)
    {
        perror("内存分配失败");
        exit(EXIT_FAILURE);
    }

    return queue;
}

// 检查队列是否为空
bool isQueueEmpty(Queue *queue)
{
    return (queue->size == 0);
}

// 检查队列是否已满
bool isQueueFull(Queue *queue)
{
    return (queue->size == queue->capacity);
}

// 入队列
void enqueue(Queue *queue, int item)
{
    if (isQueueFull(queue))
    {
        printf("队列已满\n");
        return;
    }

    queue->rear = (queue->rear + 1) % queue->capacity;
    queue->array[queue->rear] = item;
    queue->size++;
}

// 出队列
int dequeue(Queue *queue)
{
    if (isQueueEmpty(queue))
    {
        printf("队列为空\n");
        return -1;
    }

    int item = queue->array[queue->front];
    queue->front = (queue->front + 1) % queue->capacity;
    queue->size--;

    return item;
}

链式存储结构二叉树

链式存储结构构造一个二叉树就比较简单,并且无论是遍历操作还是追求最大深度,都可以比较容易的求解。
其主要的重点在于结构体的定义,链式存储结构的精髓在于,左右指针域。

// 定义二叉树结点结构
typedef struct TreeNode
{
    int data;
    struct TreeNode *left; //左右指针
    struct TreeNode *right;
} TreeNode;

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>

#define MaxSize 100
// 定义二叉树结点结构
typedef struct TreeNode
{
    int data;
    struct TreeNode *left;
    struct TreeNode *right;
} TreeNode;

// 函数声明
TreeNode *createNode(int data);
TreeNode *insert(TreeNode *root, int data);
TreeNode *search(TreeNode *root, int data);
TreeNode *findMin(TreeNode *root);
TreeNode *deleteNode(TreeNode *root, int data);
void preorderTraversal(TreeNode *root);
void inorderTraversal(TreeNode *root);
void postorderTraversal(TreeNode *root);
int maxDepthOfTree(TreeNode *root);
void levelOrderTraversal(TreeNode *root);

TreeNode *root = NULL;

int main()
{
    int choice, data;

    printf("基于链式存储结构的二叉树操作:\n");

    while (1)
    {
        printf("\n请选择操作:\n");
        printf("1. 插入\n");
        printf("2. 删除\n");
        printf("3. 查找\n");
        printf("4. 层序遍历\n");
        scanf("%d", &choice);

        switch (choice)
        {
        case 1:
            printf("输入要插入的数据: ");
            scanf("%d", &data);
            root = insert(root, data);
            printf("前序遍历结果为:");
            preorderTraversal(root);
            printf("中序遍历结果为:");
            inorderTraversal(root);
            printf("后序遍历结果为:");
            postorderTraversal(root);
            printf("二叉树最大深度为:%d\n", maxDepthOfTree(root));
            break;

        case 2:
            printf("输入要删除的数据: ");
            scanf("%d", &data);
            root = deleteNode(root, data);
            printf("前序遍历结果为:");
            preorderTraversal(root);
            printf("中序遍历结果为:");
            inorderTraversal(root);
            printf("后序遍历结果为:");
            postorderTraversal(root);
            printf("二叉树最大深度为:%d\n", maxDepthOfTree(root));
            break;

        case 3:
            printf("输入要查找的数据: ");
            scanf("%d", &data);
            TreeNode *result = search(root, data);
            if (result != NULL)
            {
                printf("找到了 %d\n", result->data);
            }
            else
            {
                printf("未找到 %d\n", data);
            }
            printf("前序遍历结果为:");
            preorderTraversal(root);
            printf("中序遍历结果为:");
            inorderTraversal(root);
            printf("后序遍历结果为:");
            postorderTraversal(root);
            printf("二叉树最大深度为:%d\n", maxDepthOfTree(root));
            break;

        case 4:
            printf("层序遍历结果为:");
            levelOrderTraversal(root);
            printf("\n");
            break;
        }
    }

    return 0;
}

// 创建新的二叉树结点
TreeNode *createNode(int data)
{
    TreeNode *newNode = (TreeNode *)malloc(sizeof(TreeNode));
    if (newNode == NULL)
    {
        perror("内存分配失败");
        exit(EXIT_FAILURE);
    }
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

// 插入结点
TreeNode *insert(TreeNode *root, int data)
{
    if (root == NULL)
    {
        return createNode(data);
    }

    if (data < root->data)
    {
        root->left = insert(root->left, data);
    }
    else if (data > root->data)
    {
        root->right = insert(root->right, data);
    }

    return root;
}

// 查找结点
TreeNode *search(TreeNode *root, int data)
{
    if (root == NULL || root->data == data)
    {
        return root;
    }

    if (data < root->data)
    {
        return search(root->left, data);
    }
    else
    {
        return search(root->right, data);
    }
}

// 找到最小值结点
TreeNode *findMin(TreeNode *root)
{
    while (root->left != NULL)
    {
        root = root->left;
    }
    return root;
}

// 删除结点
TreeNode *deleteNode(TreeNode *root, int data)
{
    if (root == NULL)
    {
        return root;
    }

    if (data < root->data)
    {
        root->left = deleteNode(root->left, data);
    }
    else if (data > root->data)
    {
        root->right = deleteNode(root->right, data);
    }
    else
    {
        // 结点有一个子结点或没有子结点
        if (root->left == NULL)
        {
            TreeNode *temp = root->right;
            free(root);
            return temp;
        }
        else if (root->right == NULL)
        {
            TreeNode *temp = root->left;
            free(root);
            return temp;
        }

        // 结点有两个子结点,找到中序遍历的后继结点(右子树中的最小值)
        TreeNode *temp = findMin(root->right);
        root->data = temp->data;
        root->right = deleteNode(root->right, temp->data);
    }

    return root;
}

// 前序遍历
void preorderTraversal(TreeNode *root)
{
    if (root != NULL)
    {
        printf("%d ", root->data);
        preorderTraversal(root->left);
        preorderTraversal(root->right);
    }
}

// 中序遍历
void inorderTraversal(TreeNode *root)
{
    if (root != NULL)
    {
        inorderTraversal(root->left);
        printf("%d ", root->data);
        inorderTraversal(root->right);
    }
}

// 后序遍历
void postorderTraversal(TreeNode *root)
{
    if (root != NULL)
    {
        postorderTraversal(root->left);
        postorderTraversal(root->right);
        printf("%d ", root->data);
    }
}

// 计算二叉树的最大深度
int maxDepthOfTree(TreeNode *root)
{
    if (root == NULL)
    {
        return 0;
    }
    else
    {
        int leftHeight = maxDepthOfTree(root->left);
        int rightHeight = maxDepthOfTree(root->right);
        return fmax(leftHeight, rightHeight) + 1;
    }
}

// 层序遍历
void levelOrderTraversal(TreeNode *root)
{
    if (root == NULL)
    {
        return;
    }

    // 创建一个队列用于层序遍历
    TreeNode *queue[MaxSize];
    int front = 0, rear = 0;

    queue[rear++] = root; // 根结点入队

    while (front < rear)
    {
        TreeNode *current = queue[front++];
        printf("%d ", current->data);

        // 左子结点入队
        if (current->left != NULL)
        {
            queue[rear++] = current->left;
        }

        // 右子结点入队
        if (current->right != NULL)
        {
            queue[rear++] = current->right;
        }
    }
}

刷题推荐

LeetCode树类型题目
树类型的题目其实是我刷的最多的,因为其实只要找到规律了,做起来就最快,直接往递归这一条路上去考虑即可。
在这里插入图片描述

408考研各数据结构C/C++代码(Continually updating)

408考研各数据结构C/C++代码(Continually updating)
这个模块是我应一些朋友的需求,希望我能开一个专栏,专门提供考研408中各种常用的数据结构的代码,并且希望我附上比较完整的注释以及提供用户输入功能,ok,fine,这个专栏会一直更新,直到我认为没有新的数据结构可以讲解了。
目前我比较熟悉的数据结构如下:
数组、链表、队列、栈、树、B/B+树、红黑树、Hash、图。
所以我会先有空更新出如下几个数据结构的代码,欢迎关注。 当然,在我前两年的博客中,对于链表、哈夫曼树等常用数据结构,我都提供了比较完整的详细的实现以及思路讲解,有兴趣可以去考古。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1076050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

忘记压缩包密码?解决方法一键找回,省时又便捷!

使用在线rar/zip解密工具&#xff0c;找回rar/zip密码并解密压缩包的方法非常简单。具体步骤如下&#xff1a;首先&#xff0c;在百度上搜索“密码帝官网”&#xff0c;这是一个专业的解密服务网站。然后&#xff0c;点击搜索结果中的链接&#xff0c;进入官网首页。在页面上方…

节能环保的选择:OLED透明拼接屏在葫芦岛市市场的成功应用

葫芦岛市位于中国辽宁省东北部&#xff0c;是一个拥有悠久历史和丰富自然景观的城市。 近年来&#xff0c;OLED透明拼接屏在葫芦岛市的市场表现备受瞩目&#xff0c;成为该市展示技术的重要代表。 OLED透明拼接屏以其出色的显示效果和高清清晰度而闻名&#xff0c;能够提供出…

大模型部署手记(9)LLaMa2+Chinese-LLaMA-Plus-7B+Windows+llama.cpp+中文文本补齐

1.简介&#xff1a; 组织机构&#xff1a;Meta&#xff08;Facebook&#xff09; 代码仓&#xff1a;GitHub - facebookresearch/llama: Inference code for LLaMA models 模型&#xff1a;llama-2-7b、Chinese-LLaMA-Plus-7B&#xff08;chinese_llama_plus_lora_7b&#x…

使用/deep/覆盖element中的样式

我们在开发Vue项目的时候一般都会在组件中的style标签上面添加scoped属性&#xff0c;从而避免父组件影响子组件的样式&#xff0c;达到只修改当前组件样式的目的。 <style scoped lang"scss">... </style> 其原理是——在打包的时候会给每个选择器都添…

什么是存储服务器?

随着互联网的发展&#xff0c;越来越多的信息会在网络上暴露&#xff0c;所以企业就会更加重视数据&#xff0c;因此更加安全可靠的数据存储服务器受到了大多数人的信赖&#xff0c;今天就让小编带大家了解一下什么是存储服务器吧&#xff01; 存储服务器的含义。存储服务器是…

基于乌燕鸥优化的BP神经网络(分类应用) - 附代码

基于乌燕鸥优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于乌燕鸥优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.乌燕鸥优化BP神经网络3.1 BP神经网络参数设置3.2 乌燕鸥算法应用 4.测试结果&#x…

常见的Web安全漏洞(2021年9月的OWASP TOP 10)

聊Web安全漏洞&#xff0c;就不得不提到OWASP TOP10。开放式Web应用程序安全项目&#xff08;OpenWeb Application Security Project&#xff0c;OWASP&#xff09;是一个开源的、非营利的组织&#xff0c;主要提供有关Web应用程序的实际可行、公正透明、有社会效益的信息&…

每日一题 2731. 移动机器人(中等,模拟)

思路: 机器人是完全相同的且移动速度一样&#xff0c;所以对于碰撞后转向的机器人&#xff0c;可以看作是不碰撞继续运动。可以这么理解&#xff0c;如果没有碰撞&#xff0c;在移动后坐标 i 处有一个机器人&#xff0c;那么存在碰撞的情况下&#xff0c;坐标 i 处任有一个机器…

全志R128芯片应用开发案例——驱动 WS2812 流水灯

驱动 WS2812 流水灯 本文案例代码下载地址驱动 WS2812 流水灯案例代码https://www.aw-ol.com/downloads?cat24 R128-DevKit 拥有4颗 WS2812 LED&#xff0c;本文将详细叙述如何点亮他们。 LEDC 模块简介 LEDC 硬件方框图如上图所示&#xff0c;CPU 通过 APB 总线操作 LEDC 寄…

使用“Apple 诊断”测试你的 Mac(查看电池是否到达更换标准)

使用“Apple 诊断”测试你的 Mac “Apple 诊断”&#xff08;以前称为“Apple Hardware Test”&#xff09;可以检查你的 Mac 是不是存在硬件问题。 如果你认为你的 Mac 可能存在硬件问题&#xff0c;可以使用“Apple 诊断”来帮助确定可能存在故障的硬件组件。“Apple 诊断”…

2023P企业管理系统提供商,助力大中型企业一体化管理,免费更新

大中型企业通常业务流程更为复杂&#xff0c;工作数据更庞大&#xff0c;数据迁移的过程中可能会遇到数据不一致、数据丢失等问题。因此大中型企业实施ERP系统是一项复杂的任务&#xff0c;需要企业投入大量的资源和精力&#xff0c;克服各种困难和挑战。 在过往为广西省大中型…

淘宝API官方商品、交易、订单、物流、插旗接口如下:

以下是一些淘宝官方商品、交易、订单、物流、插旗接口的参考&#xff1a; 获取一个产品的信息(taobao.product.get)&#xff1a;该接口可以获取一个产品的信息&#xff0c;包括商品ID、标题、价格、销量等。搜索产品信息(taobao.products.search)&#xff1a;该接口可以搜索产…

“UTONMOS”掀起元宇宙游戏热潮,全球发展前景广阔

我们都知道&#xff0c;市面上无论是PC端的网游还是移动端手游&#xff0c;它如果要做到源源不断的内容输出&#xff0c;不仅取决于游戏公司产品质量和业绩&#xff0c;也与公司的决策和市场沟通密不可分。 元宇宙游戏市场受到关注 近年来&#xff0c;元宇宙游戏市场逐渐升温…

算法题:柠檬水找零(典型的贪心算法问题)

这道题就是纯贪心算法题&#xff0c;遍历每个顾客&#xff0c;先把钱收了&#xff0c;如果是10块钱就判断手里头有没有5元用于找零&#xff1b;如果是20块钱&#xff0c;先判断是不是有10元5元&#xff0c;如果没有就再判断是否有3个5元。没有的话就直接返回 False。(完整题目附…

保研经历分享(一)

这个系列的文章主要是想记录一下自己大学期间最重要的一件事&#xff08;保研!!&#xff09;的经历、过程&#xff0c;外加一些保研流程介绍、面试经验、院校投递、踩坑经历&#xff0c;主要给学弟学妹们避雷&#xff0c;也做一些借鉴吧~ 这一篇主要是对保研过程的一些介绍&…

如何在虚幻引擎中渲染动画?

大家好&#xff0c;今天我将展示如何在虚幻引擎中渲染动画&#xff0c;以及虚幻引擎渲染动画怎么设置的方法步骤。 需要提前了解&#xff1a; 虚幻引擎本地运行慢、渲染慢、本地配置不够&#xff0c;如何解决&#xff1f; 渲云云渲染支持虚幻引擎离线渲染&#xff0c;可批量…

【UE5】Pixel Streaming 配置https协议

【UE5】Pixel Streaming 配置https协议 Pixel Streaming 传输视频流插件&#xff0c;使用任何现代Web浏览器进行连接&#xff0c;并从虚幻引擎应用程序中流式传输渲染的帧像素和音频。 用户无需安装或下载任何内容。 HTTPS &#xff08;全称&#xff1a;Hypertext Transfer Pro…

大模型部署手记(12)LLaMa2+Chinese-LLaMA-Plus-2-7B+Windows+text-gen+中文对话

1.简介&#xff1a; 组织机构&#xff1a;Meta&#xff08;Facebook&#xff09; 代码仓&#xff1a;https://github.com/facebookresearch/llama 模型&#xff1a;chinese-alpaca-2-7b-hf 下载&#xff1a;使用百度网盘下载 硬件环境&#xff1a;暗影精灵7Plus Windows版…

“套壳”OpenAI,注定消亡!全球首个 20 万字大模型发布丨 RTE 开发者日报 Vol.63

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE &#xff08;Real Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有…