数据结构---手撕图解二叉树(含大量递归图解)

这篇具有很好参考价值的文章主要介绍了数据结构---手撕图解二叉树(含大量递归图解)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写在前面

二叉树的几乎所有实现都是依靠递归实现,递归的核心思路是把任何一个二叉树看成根和左右子树,而二叉树递归的核心玩法就是把二叉树的左右子树再看成根,再找左右子树,再看成根…

因此,解决二叉树问题实际上要把二叉树转换成一个一个子树的过程,找到一个一个的子树再组装起来就形成了二叉树

二叉树的创建

二叉树建立的正统方法是利用递归,这里展示递归的一种写法

BTNode* BuyNode(BTDataType a)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	newnode->data = a;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}

BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}

	BTNode* root = BuyNode(a[*pi]);
	(*pi)++;

	root->left = BinaryTreeCreate(a, pi);
	root->right = BinaryTreeCreate(a, pi);

	return root;
}

如果你对递归的认识并不熟悉,下面我画了一幅递归展开图,更好解释这其中的原理

数据结构---手撕图解二叉树(含大量递归图解),数据结构,知识总结,数据结构,笔记
(右子树的部分过程由于空间原因不够完全展开,但如果看懂左子树的构建过程,右子树不难自己画出)

这里注意的是,函数栈帧的创建并不是每一个都不一样的位置,当一个栈帧被销毁后,另外一个栈帧创建就会在原来的位置,因此在计算空间复杂度的时候需要考虑这个问题:
空间是可以重复利用的,时间是一去不复返的

从这幅图中,相信可以理解这段代码的含义了

首先创建一个根节点,根节点去寻找左子树进入递归,而进入递归后继续寻找左子树…直到遇到NULL截止,而遇到空后就寻找右子树,右子树也找到空后返回,这样就构建出了二叉树中最小的一棵树,而这棵树会返回,作为根的左子树,然后继续进入递归寻找根的右子树…

由于是借助链表进行实现的,因此我们可以理解为首先创建好根,而根的左右子树用函数创建好后再连接起来…当遇到#就返回,形成了一个一个的小树,小树最后组成大树,就形成了二叉树

二叉树的遍历

二叉树的遍历主要有前序遍历,中序遍历,后序遍历,层序遍历

前序遍历

前序遍历是指遍历时先访问根,再访问左子树,再访问右子树

代码实现如下

void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	printf("%c ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

下面依旧画出它的递归展开图

数据结构---手撕图解二叉树(含大量递归图解),数据结构,知识总结,数据结构,笔记

中序遍历

中序遍历是先访问左子树,再访问根,最后访问右子树

void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	BinaryTreeInOrder(root->left);
	printf("%c ", root->data);
	BinaryTreeInOrder(root->right);
}

递归图和上面基本类似,就不做画出了

后序遍历

后序遍历是先访问左子树,再访问右子树,最后访问根

void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%c ", root->data);
}

递归图和上面基本类似,就不做画出了

层序遍历

层序遍历指的是对二叉树进行一层一层的遍历,每一层分别遍历,这里借助队列进行遍历,基本思路把根放到队列中,当某一个根要出队列时,就令该根的左子树和右子树进队列,这样就能实现一层一层遍历,画法如下:

数据结构---手撕图解二叉树(含大量递归图解),数据结构,知识总结,数据结构,笔记
代码实现相较于前面来说简单一些,但需要引入队列的,关于队列的介绍:

数据结构—手撕队列栈并相互实现

下面展示要使用的队列的相关函数实现

// queue.h
typedef struct BinaryTreeNode* QDataType;

typedef struct QNode
{
	QDataType data;
	struct QNode* next;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

// queue.c
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

QNode* BuyQnode(QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = BuyQnode(x);
	if (pq->ptail == NULL)
	{
		assert(pq->phead == NULL);
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

bool QueueEmpty(Queue* pq)
{
	if (pq->size == 0)
	{
		return true;
	}
	return false;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		QNode* newhead = pq->phead->next;
		free(pq->phead);
		pq->phead = newhead;
	}

	pq->size--;
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->phead->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	return pq->ptail->data;
}

int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

那么借助队列我们来实现刚才的层序遍历

void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%c ", front->data);
		QueuePop(&q);
		if (front->left)
			QueuePush(&q, front->left);
		if(front->right)
			QueuePush(&q, front->right);
	}
	printf("\n");
	//BinaryTreeDestory(&q);
}

二叉树的销毁

在知道了遍历的多种途径后,二叉树的销毁就很简单了,在确定代码如何实现前,先思考问题:应该选用哪种遍历来进行二叉树的销毁?

结果是很明显的,选用后序遍历是最方便的,原因在于销毁是需要进行节点的释放的,如果使用前序或者中序遍历,那么在遍历的过程中根节点会被先销毁,那么找左子树或者右子树就带来了不便(可以定义一个变量保存位置),因此最好用后序遍历,把子树都销毁了最后销毁根

void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}

	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}

二叉树节点个数

二叉树节点个数也是转换成子树来解决,如果为NULL就返回0,其他情况返回左右相加再加上节点本身

int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

二叉树叶子节点的个数

叶子节点个数求解和上述方法相似,也是找最小的子树,但不同的地方是,叶子节点找到的条件是叶子作为根,它的左右子树都为空,符合这样的就是叶子节点,那么根据这个想法求得代码不难得到:

int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}

	int leftleave = BinaryTreeSize(root->left);
	int rightleave = BinaryTreeSize(root->right);

	return leftleave + rightleave;
}

二叉树查找值为x的节点

这个相对较麻烦一点,先上展开图和代码

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}

	if (root->data == x)
	{
		return root;
	}

	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)
		return ret1;
	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
		return ret2;

	return NULL;
}

数据结构---手撕图解二叉树(含大量递归图解),数据结构,知识总结,数据结构,笔记
这里的关键动作就是判断ret1和ret2是否为空,如果是,则说明找到正确的值,那么就会一路被送出函数,相反会一直返回NULL,因此就不必担心送出其他元素的情况

二叉树是否为完全二叉树

这个思路也较为复杂,首先,需要解决的问题是用什么思路,基本思路是一个一个看,如果在某一个节点已经为空的前提下,它后面的节点还不为空,那么就说明这个不是完全二叉树

因此这个思路是不是和层序遍历很像呢?我们借助这样的思路继续写下去

当队列不为空就继续入队列出队列进行层序遍历,当遇到空就跳出,跳出循环后此时队列并不为空,继续出队列,如果发现某个元素不为空,那么就证明这并不是完全二叉树

代码实现如下:

int BinaryTreeComplete(BTNode* root)
{
	assert(root);
	Queue q;
	QueueInit(&q);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
		QueuePush(&q,front->left);
		QueuePush(&q,front->right);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			BinaryTreeDestory(&q);
			return 0;
		}
	}
	BinaryTreeDestory(&q);
	return 1;
}

至此,二叉树的基本功能实现就都结束了,很明显,二叉树的实现并不容易,它不仅需要对递归有一定深度的认识,还需要你对队列有足够的理解,才能解决层序遍历和判断完全二叉树

这需要进行更多的练习,也为后面更复杂的树打基础和铺垫

数据结构---手撕图解二叉树(含大量递归图解),数据结构,知识总结,数据结构,笔记文章来源地址https://www.toymoban.com/news/detail-576796.html

到了这里,关于数据结构---手撕图解二叉树(含大量递归图解)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构】---几分钟简单几步学会手撕链式二叉树(上)

    👧个人主页:@小沈熬夜秃头中୧⍤⃝❅ 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:数据结构 🔑本章内容:手撕链式二叉树 送给各位💌:我从没觉得孤独 说的浪漫点 我完全自由 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 提示:以下是本篇文章正文内容,下

    2024年02月07日
    浏览(35)
  • 【数据结构】---几分钟简单几步学会手撕链式二叉树(下)

    👧个人主页:@小沈熬夜秃头中୧⍤⃝❅ 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:数据结构 🔑本章内容:手撕链式二叉树 送给各位💌:成为更好的自己才是应该做的事 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 提示:以下是本篇文章正文内容,下面案例可

    2024年02月08日
    浏览(41)
  • 【数据结构】二叉树篇|超清晰图解和详解:二叉树的序列化和反序列化

    博主简介: 努力学习的22级计算机科学与技术本科生一枚🌸 博主主页: @是瑶瑶子啦 每日一言🌼: 你不能要求一片海洋,没有风暴,那不是海洋,是泥塘——毕淑敏 🍊 序列化 :本质就是 二叉树的遍历 ,就那么几个:前序、中序、后序、层序。而序列化只不过就是 在遍历到

    2024年02月10日
    浏览(53)
  • 数据结构刷题篇 之 【力扣二叉树基础OJ】详细讲解(含每道题链接及递归图解)

    有没有一起拼用银行卡的,取钱的时候我用,存钱的时候你用 难度等级:⭐ 直达链接:相同的树 难度等级:⭐ 直达链接:单值二叉树 难度等级:⭐⭐ 直达链接:对称二叉树 难度等级:⭐⭐⭐ 直达链接:二叉树的前序遍历 难度等级:⭐⭐⭐⭐ 直达链接:另一颗子树 注:

    2024年04月16日
    浏览(79)
  • 手撕二叉树(图解+代码)

    在学习二叉树之前,我们有必要了解一下树的概念和常用术语。接下来以下面这棵树为例来回顾下树相关概念: 树的概念:一种非线性的数据结构,由n(n =0)个节点组成的一个具有层次关系的集合。 树的术语: 节点的度 一个节点含有子树的个数,例如上图中A的度为6. 树的度

    2023年04月22日
    浏览(36)
  • 数据结构---手撕图解双向循环链表

    在前面学完单链表后,我们思考这样一个问题,单链表和顺序表比起来,功能确实相当强大,有很多优势,但是于此同时,我们也应思考下面的问题 单链表有什么不足的地方? 如果你把单链表的各个函数都自己实现过,那么下面的问题你一定有相同的感悟 单链表实现尾插尾

    2024年02月15日
    浏览(57)
  • 数据结构:手撕图解双向循环链表

    在前面学完单链表后,我们思考这样一个问题,单链表和顺序表比起来,功能确实相当强大,有很多优势,但是于此同时,我们也应思考下面的问题 单链表有什么不足的地方? 如果你把单链表的各个函数都自己实现过,那么下面的问题你一定有相同的感悟 单链表实现尾插尾

    2024年02月15日
    浏览(88)
  • 数据结构:手撕图解七大排序(含动图演示)

    插入排序分为直接插入排序和希尔排序,其中希尔排序是很值得学习的算法 希尔排序的基础是直接插入排序,先学习直接插入排序 直接插入排序类似于打扑克牌前的整牌的过程,假设我们现在有2 4 5 3四张牌,那么应该怎么整牌? 方法很简单,把3插到2和4中间,这样就完成了

    2024年02月15日
    浏览(51)
  • 数据结构:手撕图解堆的实现和TopK的应用

    要讲到堆,先要说两个关于二叉树的概念 满二叉树:一个二叉树如果每一层的节点数都是最大值,那么这个二叉树就是满二叉树 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是满二叉树的变形,对于深度为k的树有n个节点的二叉树,当且仅当其每一个节点都与

    2024年02月15日
    浏览(48)
  • 数据结构---手撕图解堆的实现和TopK的应用

    要讲到堆,先要说两个关于二叉树的概念 满二叉树:一个二叉树如果每一层的节点数都是最大值,那么这个二叉树就是满二叉树 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是满二叉树的变形,对于深度为k的树有n个节点的二叉树,当且仅当其每一个节点都与

    2024年02月16日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包