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

文章目录

  • 顺序存储结构二叉树
  • 链式存储结构二叉树
  • 刷题推荐
  • 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 100typedef 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+1initTree(&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.rhkb.cn/news/154638.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MYSQL的日志管理

MySQL中有几种类型的日志记录&#xff0c;分别用于记录不同的操作和事件。以下是MySQL中常见的日志类型 错误日志 错误日志是 MySQL 中最重要的日志之一&#xff0c;它记录了当 mysqld 启动和停止时&#xff0c;以及服务器在运行过程中发生任何严重错误时的相关信息。当数据…

linux后台运行java项目/ jar包:nohup 命令

1.提出问题 我们把一个 SpringBoot 工程导出为 jar 包&#xff0c;jar 包上传到阿里云 ECS 服务器上&#xff0c;使用 java -jar xxx-xxx.jar 命令启动这个 SpringBoot 程序。此时我们本地的 xshell 客户端必须一直开着&#xff0c;一旦 xshell 客户端关闭&#xff0c;java -j…

Jenkins对应java版本

官网地址&#xff1a;Java Support Policy 运行jenkins时,需要使用下列Java版本:

Jenkins安装多个jdk版本,并在项目中选择对应jdk版本

下载jdk版本&#xff1a;进入oracle官网下载官方jdk Java Downloads | Oracle 例&#xff1a;比如项目需要使用java8.341的版本&#xff0c;而jenkins用的是java11的版本&#xff0c;这里就需要下载多个jdk版本。进入下载网址&#xff0c;Java Archive Downloads - Java SE 8u…

MySQL数据库技术笔记(3)

概述 学习MySQL数据库技术其实只需要安装mysql服务器就可以使用了。只不过对于初学者来说直接操作dos窗口方式比较麻烦&#xff0c;命令不熟悉&#xff0c;导致经常写错。在真实的开发当中直接操作dos窗口效率比较慢&#xff0c;企业中也会经常使用一些mysql数据库支持的可视化…

学习记忆——数学篇——案例——代数——方程——一元二次方程

重点记忆法 a x 2 b x c 0 ax^2bxc0 ax2bxc0 整体可以由&#xff1a; 根&#xff08;多少&#xff0c;正负&#xff0c;区间&#xff09; ⟹ \Longrightarrow ⟹ △ △ △ ⟹ \Longrightarrow ⟹ 求根公式 x 1 , 2 x_{1,2} x1,2​ − b △ 2 a \frac{-b\sqrt{△}}{2a} 2…

Transformer模型 | Python实现TransformerCPI模型(pytorch)

文章目录 效果一览文章概述程序设计参考资料效果一览 文章概述 Python实现TransformerCPI模型(tensorflow) Dependencies: python 3.6 pytorch >= 1.2.0 numpy RDkit = 2019.03.3.0 pandas Gensim >=3.4.0 程序设计 import torch import numpy as np import random …

TensorFlow入门(十九、softmax算法处理分类问题)

softmax是什么? Sigmoid、Tanh、ReLU等激活函数,输出值只有两种(0、1,或-1、1或0、x),而实际现实生活中往往需要对某一问题进行多种分类。例如之前识别图片中模糊手写数字的例子,这个时候就需要使用softmax算法。 softmax的算法逻辑 如果判断输入属于某一个类的概率大于属于其…

线性代数 --- 矩阵的QR分解,A=QR

矩阵的QR分解&#xff0c;格拉姆施密特过程的矩阵表示 首先先简单的回顾一下Gram-Schmidt正交化过程的核心思想&#xff0c;如何把一组线性无关的向量构造成一组标准正交向量&#xff0c;即&#xff0c;如何把矩阵A变成矩阵Q的过程。 给定一组线性无关的向量a,b,c&#xff0c;我…

Hazelcast系列(三):hazelcast集成(服务器/客户端)

系列文章 Hazelcast系列(一)&#xff1a;初识hazelcast Hazelcast系列(二)&#xff1a;hazelcast集成&#xff08;嵌入式&#xff09; Hazelcast系列(三)&#xff1a;hazelcast集成&#xff08;服务器/客户端&#xff09; Hazelcast系列(四)&#xff1a;hazelcast管理中心 …

ubuntu下使用gcc编译c程序: “error: stray ‘\357’ in program“

现象&#xff1a; ubuntu下使用gcc编译c程序: “error: stray ‘\357’ in program“ 尝试查找原因&#xff1a;打开从windos直接粘贴c程序到ubuntu的c代码&#xff0c;发现多了 <200b>&#xff1a; 方案&#xff1a;尝试在vim编辑器删除&#xff0c;多出来的字符后编译…

长沙建筑模板生产厂家有哪些?

在湖南长沙地区&#xff0c;建筑施工企业寻找一家可信赖的建筑模板供应商是非常重要的。在长沙地区&#xff0c;有多家建筑模板生产厂家&#xff0c;其中值得一提的是能强优品木业&#xff0c;他们是长沙地区建筑模板生产的领先供应商之一。 能强优品木业位于广西贵港市&#x…

Linux 部署1Panel 现代化运维管理面板进行公网远程访问

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透2.1 使用一键脚本安装命令 2.2向系统添加服务2.3 启动cpolar服务…

【Java】 DirectByteBuffer堆外内存回收

PhantomReference虚引用 在分析堆外内存回收之前&#xff0c;先了解下PhantomReference虚引用。 PhantomReference需要与ReferenceQueue引用队列结合使用&#xff0c;在GC进行垃圾回收的时候&#xff0c;如果发现一个对象只有虚引用在引用它&#xff0c;则认为该对象需要被回…

PyTorch 入门

一、说明 深度学习是机器学习的一个分支&#xff0c;其中编写的算法模仿人脑的功能。深度学习中最常用的库是 Tensorflow 和 PyTorch。由于有各种可用的深度学习框架&#xff0c;人们可能想知道何时使用 PyTorch。以下是人们更喜欢使用 Pytorch 来完成特定任务的原因。 Pytorch…

虹科分享 | 确保冻干工艺开发中精确测量和数据完整性的5步指南

虹科分享 | 确保冻干工艺开发中精确测量和数据完整性的5步指南 介绍 冻干周期的工艺开发在冻干中起着至关重要的作用&#xff0c;因为它可以优化关键工艺参数&#xff0c;以实现理想的产品质量和工艺一致性。优化冻干工艺还可以缩短运行时间&#xff0c;尽早发现关键错误&…

Unity头发飘动效果

Unity头发飘动 介绍动作做头发飘动头发骨骼绑定模拟物理组件 UnityChan插件下载UnityChan具体用法确定人物是否绑定好骨骼节点&#xff08;要做的部位比如头发等&#xff09;给人物添加SpringManager骨骼管理器给骨骼节点添加SpringBone这里给每个头发骨骼都添加上SpringBone。…

【RabbitMQ 实战】09 客户端连接集群生产和消费消息

一、部署一个三节点集群 下面的链接是最快最简单的一种集群部署方法 3分钟部署一个RabbitMQ集群 上的的例子中&#xff0c;没有映射端口&#xff0c;所以没法从宿主机外部连接容器&#xff0c;下面的yml文件中&#xff0c;暴露了端口。 每个容器应用都映射了宿主机的端口&…

Excel 快速分析

文章目录 格式化图表汇总计数 表超级表 迷你图 快捷键: Ctrl Q 先选中数据, 再按快捷键或快速分析按钮. 格式化 查看规则: 前提是先在表中添加某种规则, 再全选该表, 这样在查看规则时才会显示出这个规则. 图表 汇总 计数 表 超级表 迷你图

Stable Diffusion生成图片

画质 masterpiece,best quality,illustration,extremely detail CG unity 8k wallpaper,ultra-detailed,depth of field 杰作&#xff0c;最佳质量&#xff0c;插图&#xff0c;极度详细的8K壁纸&#xff0c;超高详细度&#xff0c;景深 画风 Chinese ink painting,water color…