数据结构(初阶6)---二叉树(遍历——递归的艺术)(详解)

ops/2024/11/28 18:46:05/

二叉树的遍历与练习

  • 一.二叉树的基本遍历形式
    • 1.前序遍历(深度优先遍历)
    • 2.中序遍历(深度优先遍历)
    • 3.后序遍历(深度优先遍历)
    • 4.层序遍历!!(广度优先遍历)
  • 二.二叉树的leetcode小练习
    • 1.判断平衡二叉树
      • 1)正常解法
      • 2)优化解法
    • 2.对称二叉树

通过上一篇文章,我们初识了我们的二叉树
二叉树初识
那么接下来我们就要深入去理解二叉树的部分知识了,显然这只是在二叉树家族中迈出的一小步qwq,入门。

一.二叉树的基本遍历形式

我们先建立一棵伪树,方便我们后续的使用:请添加图片描述

int main()
{BinaryTree p1;BinaryTree p2;BinaryTree p3;BinaryTree p4;BinaryTree p5;BinaryTree p6;p1.left = &p2;p1.right = &p3;p2.left = NULL;p2.right = &p4;p3.left = &p5;p3.right = NULL;p4.left = NULL;p4.right = &p6;p6.left = NULL;p6.right = NULL;p5.left = NULL;p5.right = NULL;p1.val = 'A';p2.val = 'B';p3.val = 'C';p4.val = 'D';p5.val = 'E';p6.val = 'F';
}

1.前序遍历(深度优先遍历)

前序遍历的本质,就是根节点->左孩子->右孩子。并且通过递归调用的方式去实现。
请添加图片描述

void treefront(BinaryTree* p)//前序遍历
{if (p == NULL){printf("NULL ");return;}printf("%c ", p->val);treefront(p->left);treefront(p->right);}

2.中序遍历(深度优先遍历)

同理,中序遍历的本质就是左孩子->根节点->右孩子
如:
请添加图片描述

void treemid(BinaryTree* p)//中序遍历
{if (p == NULL){printf("NULL ");return;}treemid(p->left);printf("%c ", p->val);treemid(p->right);}

3.后序遍历(深度优先遍历)

同理,后序遍历的本质就是:左孩子->右孩子->根节点
如:
请添加图片描述

void treebehind(BinaryTree* p)//后序遍历
{if (p == NULL){printf("NULL ");return;}treebehind(p->left);treebehind(p->right);printf("%c ", p->val);
}

4.层序遍历!!(广度优先遍历)

层序遍历与以上三种遍历方式完全不同,他没有使用递归的思想,而是去使用了迭代的方法:
请添加图片描述
这里我们将使用我们先前学到的循环队列的数据结构去完成二叉树的层序遍历
逻辑如下:
运用队列的先进先出的特点
1.我们先塞入第一个根节点,
2.我们取出队列排头的节点的时候,自动往队列里面添加两个他的儿子节点
3.当队列里面为空的时候,我们就完成了一次层序遍历
请添加图片描述
接下来我们进行代码实现:
1.伪树

int main()
{BinaryTree p1;BinaryTree p2;BinaryTree p3;BinaryTree p4;BinaryTree p5;BinaryTree p6;p1.left = &p2;p1.right = &p3;p2.left = NULL;p2.right = &p4;p3.left = &p5;p3.right = NULL;p4.left = NULL;p4.right = &p6;p6.left = NULL;p6.right = NULL;p5.left = NULL;p5.right = NULL;p1.val = 'A';p2.val = 'B';p3.val = 'C';p4.val = 'D';p5.val = 'E';p6.val = 'F';
}

2.层序遍历

#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef BinaryTree* QueueDataType;
typedef struct CirQueue
{QueueDataType* q;int front;int rear;int capacity;
}CirQueue;QueueDataType Cirqueuefront(CirQueue* p1)
{return *((p1->q)+(p1->front));
}CirQueue* CirQueueCreate(CirQueue* p,size_t x)
{p = (CirQueue*)malloc(sizeof(CirQueue));p->q = (QueueDataType*)(malloc(sizeof(QueueDataType) * x));p->capacity = x;p->front = 0;p->rear = 0;
}
int isCirQueueEmpty(CirQueue* p)
{if (p->front == p->rear){return 1;}else{return 0;}
}
int isCirQueueFull(CirQueue* p)
{if ((p->rear+1) % p->capacity == p->front){return 1;}else{return 0;}
}
void CirQueuePop(CirQueue* p)
{if (!isCirQueueEmpty(p)){p->front=(p->front+1)%p->capacity;}else{return;}
}
void CirQueuePush(CirQueue* p,QueueDataType x)
{if (isCirQueueFull(p)){return;}else{*(p->q + p->rear) = x;p->rear = (p->rear + 1) % p->capacity;}
}void treeseq(BinaryTree* p)//层序遍历
{CirQueue* que=NULL;que = CirQueueCreate(que, 20);CirQueuePush(que, p);while (!isCirQueueEmpty(que)){if (Cirqueuefront(que) != NULL){printf("%c ", Cirqueuefront(que)->val);CirQueuePush(que, Cirqueuefront(que)->left);CirQueuePush(que, Cirqueuefront(que)->right);}else{printf("NULL ");}CirQueuePop(que);}
}

在这里插入图片描述

leetcode_225">二.二叉树的leetcode小练习

1.判断平衡二叉树

在这里插入图片描述
平衡二叉树:当二叉树的每一个节点的两个子树的深度的差的绝对值小于1,则称为平衡二叉树。

1)正常解法

1.先创造求深度函数

int depthtree(struct TreeNode* root)
{if(root==NULL){return 0;}int leftdepth=depthtree(root->left);int rightdepth=depthtree(root->right);return 1+(leftdepth>rightdepth?leftdepth:rightdepth);
}

再通过分解子问题
求大树是否为平衡二叉树,我们先求解两个子树是不是平衡二叉树

bool isBalanced(struct TreeNode* root) {if(root==NULL){return true;}int left=depthtree(root->left);int right=depthtree(root->right);bool x=(left-right<=1)&&(left-right>=-1);if(!x){return false;}return isBalanced(root->left)&&isBalanced(root->right);
}

2)优化解法

刚刚的那种解法是一种低效的解法,通过前序遍历我们进行了很多次重复的计算。
所以我们考虑一下,是否可以使用后序遍历,
先快速来到底部,从底部向上走,而每一次的树的深度就可以直接将当前子树的高度++即可

bool _isBalanced(struct TreeNode* root,int* pdepth)
{if(root==NULL){return true;}int depth_left=0;int depth_right=0;if(!_isBalanced(root->left,&depth_left)){return false;}if(!_isBalanced(root->right,&depth_right)){return false;}if(abs(depth_right-depth_left)>1){return false;}*pdepth=1+(depth_right>depth_left?depth_right:depth_left);return true;
}
bool isBalanced(struct TreeNode* root) {int depth=0;return _isBalanced(root,&depth);
}

在这里插入图片描述

这样子我们只需要遍历n次,时间复杂度O(n)即可解决问题

2.对称二叉树

在这里插入图片描述
通过相反的遍历比较,可以得出是否为二叉树

bool issame(struct TreeNode* p1,struct TreeNode* p2)
{if(p1==NULL&&p2==NULL){return true;}else if((p1==NULL&&p2!=NULL)||(p1!=NULL&&p2==NULL)){return false;}return (p1->val==p2->val)&&issame(p1->left,p2->right)&&issame(p2->left,p1->right);
}
bool isSymmetric(struct TreeNode* root) {if(root==NULL){return true;}return issame(root->left,root->right);}

在这里插入图片描述

ps:创作不易,恳请留一个赞吧


http://www.ppmy.cn/ops/137437.html

相关文章

索尼欲推新一代PSP/PSV掌机,将支持PS4/5游戏

原文转载修改自&#xff08;更多互联网新闻/搞机小知识&#xff09;&#xff1a; 新一代索尼掌机将支持PS4/PS5游戏&#xff0c;或与PS6同时期推出 说起索尼掌机&#xff0c;很难逃得过一句&#xff1a;怒其不争。PSP、PSV打下的大好河山&#xff0c;愣是断送在时代洪流的大浪…

UE5 Spawm Emitter at Location(在位置处生成发射器)

在 Unreal Engine 5 (UE5) 中&#xff0c;Spawn Emitter at Location 是一个非常有用的节点&#xff0c;用来在特定位置生成粒子效果&#xff08;Particle Emitter&#xff09;。这个节点常用于在蓝图中创建临时的粒子效果&#xff0c;例如爆炸、火花或其他动态效果。 如何使用…

docker 安装mysql8.4.0

1、拉取mysql8.4.0镜像 docker pullmysql:8.4.0-oraclelinux8查看镜像 docker images2、新建宿主机本地目录&#xff1a;用来挂载MySQL容器所产生的数据的目录 mkdir -p /home/admin/data/mysql /home/admin/logs/mysql /home/admin/conf/mysql3、在/home/admin/conf/mysql目…

HTTP 缓存技术

HTTP 缓存技术 1. 缓存概述 HTTP 缓存技术通过存储已请求资源的副本&#xff0c;减少重复请求、提升响应速度&#xff0c;并节省带宽。缓存可以在客户端、代理服务器、CDN&#xff08;内容分发网络&#xff09;等位置进行&#xff0c;能够有效提升 Web 应用的性能、降低服务器…

限制对 etcd 的访问范围是确保 Kubernetes 集群安全的一个重要环节。

限制对 etcd 的访问范围是确保 Kubernetes 集群安全的一个重要环节。通常&#xff0c;etcd 只应当对 Kubernetes 控制平面的组件&#xff08;如 API Server、Controller Manager、Scheduler 等&#xff09;以及某些维护工具&#xff08;如备份工具&#xff09;开放访问权限&…

泷羽sec学习打卡-shell命令2

声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 关于shell的那些事儿-shell2 临时变量和永久变量为什么使用ls、dir命令可以输出一些内容呢&#xff1f;如…

java 集合 菱形内定义封装类 而非基本数据类型 原因解释 详解

在 Java 中&#xff0c;泛型&#xff08;例如 List<E>、Map<K, V>&#xff09;要求使用封装类&#xff08;Wrapper Class&#xff09;而不是基本数据类型&#xff08;Primitive Types&#xff09;。这是因为 Java 泛型的实现机制&#xff08;基于类型擦除&#xff…

Vue 是如何实现数据双向绑定的?

前言 Vue.js 核心特性之一是数据双向绑定&#xff08;Two-way Data Binding&#xff09;&#xff0c;这一特性不仅简化了开发者与数据交互的过程&#xff0c;还大大提升了开发效率和用户体验。那么在 Vue.js 的内部机制中&#xff0c;数据双向绑定究竟是如何实现的呢&#xff…