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

二叉树的遍历与练习

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

通过上一篇文章,我们初识了我们的二叉树
二叉树初识

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

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

我们先建立一棵伪树 ,方便我们后续的使用:

c 复制代码
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.前序遍历(深度优先遍历)

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

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

}

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

同理,中序遍历的本质就是左孩子->根节点->右孩子

如:

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

}

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

同理,后序遍历的本质就是:左孩子->右孩子->根节点

如:

c 复制代码
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.伪树

C 复制代码
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.层序遍历

c 复制代码
#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小练习

1.判断平衡二叉树

平衡二叉树:当二叉树的每一个节点的两个子树的深度的差的绝对值小于1,则称为平衡二叉树。

1)正常解法

1.先创造求深度函数

c 复制代码
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);
}

再通过分解子问题

求大树是否为平衡二叉树,我们先求解两个子树是不是平衡二叉树

c 复制代码
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)优化解法

刚刚的那种解法是一种低效的解法,通过前序遍历我们进行了很多次重复的计算。

所以我们考虑一下,是否可以使用后序遍历,
先快速来到底部,从底部向上走,而每一次的树的深度就可以直接将当前子树的高度++即可

c 复制代码
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.对称二叉树

通过相反的遍历比较,可以得出是否为二叉树

c 复制代码
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:创作不易,恳请留一个赞吧

相关推荐
算AI13 小时前
人工智能+牙科:临床应用中的几个问题
人工智能·算法
我不会编程55514 小时前
Python Cookbook-5.1 对字典排序
开发语言·数据结构·python
李少兄14 小时前
Unirest:优雅的Java HTTP客户端库
java·开发语言·http
无名之逆14 小时前
Rust 开发提效神器:lombok-macros 宏库
服务器·开发语言·前端·数据库·后端·python·rust
似水এ᭄往昔14 小时前
【C语言】文件操作
c语言·开发语言
啊喜拔牙14 小时前
1. hadoop 集群的常用命令
java·大数据·开发语言·python·scala
owde15 小时前
顺序容器 -list双向链表
数据结构·c++·链表·list
xixixin_15 小时前
为什么 js 对象中引用本地图片需要写 require 或 import
开发语言·前端·javascript
第404块砖头15 小时前
分享宝藏之List转Markdown
数据结构·list
W_chuanqi15 小时前
安装 Microsoft Visual C++ Build Tools
开发语言·c++·microsoft