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

这篇具有很好参考价值的文章主要介绍了【数据结构】---几分钟简单几步学会手撕链式二叉树(上)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

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


提示:以下是本篇文章正文内容,下面案例可供参考

🌟一、二叉树链式结构的实现

🌏1.1 前置说明

在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。

💫快速创建一棵简单的二叉树

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

BTNode*BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}
BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);

	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return node1;
}

注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后序详解重点讲解。
再看二叉树基本操作前,再回顾下二叉树的概念:

二叉树是:

  1. 空树
  2. 非空:根节点,根节点的左子树、根节点的右子树组成的。

从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。【数据结构】---几分钟简单几步学会手撕链式二叉树(上)

🌏1.2 二叉树的遍历的时间、空间复杂度

时间复杂度:O(N)
空间复杂度:O(h)—h是高度范围是【logN,N】

🌏1.3 二叉树的遍历

💫1.3.1 前序、中序以及后序遍历:

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历

💫1.3.2 前序遍历:

(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前(根 -> 左子树 -> 右子树)。

📒代码:
void PrevOrder(BTNode* root)//前序
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right );
}
📒流程图:

结果:1 2 3 NULL NULL NULL 4 5 NULL NULL 6 NULL NULL【数据结构】---几分钟简单几步学会手撕链式二叉树(上)

💫1.3.3 后序遍历

(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后(左子树 -> 右子树 -> 根)。

📒代码:
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}
📒流程图:

只画了部分,具体和第一个前序一样的流程只不过注意到底是先走哪里
结果:NULL NULL 3 NULL 2 NULL NULL 5 NULL NULL 6 4 1
【数据结构】---几分钟简单几步学会手撕链式二叉树(上)

💫1.3.4 中序遍历:就不画流程图了具体即上有兴趣可以自己画一下

(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中间(左子树 -> 根 -> 右子树)。
结果:NULL 3 NULL 2 NULL 1 NULL 5 NULL 4 NULL 6 NULL

📒代码:
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

🌏1.4 二叉树节点个数

💫1.4.1 错误示范一(代码):

📒代码:
void BTreeSzie(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	int size = 0;
	size++;
	BTreeSzie(root->left );
	BTreeSzie(root->right );
}
📒流程图:

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

💫1.4.2 错误示范二(代码):

📒代码:

经过错误示范一想要改进就可能想到static—C语言关键字
虽然解决了size每次调用重定义的错误但是我们并不能拿到size,因为size是局部变量,为了解决这个问题,就可能想到返回值的做法,但是返回值会导致调用多次这个函数时,static一直向后面增加而外面也不能局部变量置零

int BTreeSzie(BTNode* root)
{
	static int size = 0;
	printf("%p,%d\n", &size,size);
	if (root == NULL)
	{
		return size;
	}
	size++;
	BTreeSzie(root->left );
	BTreeSzie(root->right );
	return size;
}
📒流程图:

通过打印size的地址和值可以得知static解决了错误示范一中的问题【数据结构】---几分钟简单几步学会手撕链式二叉树(上)

但是出现多次调用还是会出现累加现象同时不能在下一次调用这个函数时将size置零,因为它是一个局部变量。【数据结构】---几分钟简单几步学会手撕链式二叉树(上)

💫1.4.3 正确代码第一种(方式):定义全局变量

📒代码:
int size = 0;
void BTreeSzie(BTNode* root)
{
	if (root == NULL)
	{
		return size;
	}
	size++;
	BTreeSzie(root->left);
	BTreeSzie(root->right);
	return size;
}
📒流程图:

多次调用可以在第二次调用时将第一次数据置零【数据结构】---几分钟简单几步学会手撕链式二叉树(上)

💫1.4.4 正确代码第二种(方式):

📒代码:
int BTreeSzie(BTNode* root)
{
	return root == NULL ? 0 : BTreeSzie(root->left) + BTreeSzie(root->right) + 1;
}
📒流程图:

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

🌟二、全部代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>
#include<stdio.h>
#include<assert.h>

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

BTNode*BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}
BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);

	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return node1;
}


void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

//二叉树节点个数 --- 遍历计数
//int size = 0;
//void BTreeSzie(BTNode* root)
//{
//	if (root == NULL)
//	{
//		return size;
//	}
//	size++;
//	BTreeSzie(root->left);
//	BTreeSzie(root->right);
//	return size;
//}

//int BTreeSzie(BTNode* root)
//{
//	static int size = 0;
//	//printf("%p,%d\n", &size,size);
//	if (root == NULL)
//	{
//		return size;
//	}
//	size++;
//	BTreeSzie(root->left );
//	BTreeSzie(root->right );
//	return size;
//}


int BTreeSzie(BTNode* root)
{
	return root == NULL ? 0 : 
		BTreeSzie(root->left) 
		+ BTreeSzie(root->right) + 1;
}


int main()
{
	BTNode* root = CreatBinaryTree();
	PrevOrder(root);
	printf("\n");
	InOrder(root);
	printf("\n");
	PostOrder(root);
	printf("\n");
	//printf("BTreeSize:%d\n", BTreeSzie(root));
	//printf("BTreeSize:%d\n", BTreeSzie(root));
	//printf("BTreeSize:%d\n", BTreeSzie(root));
	/*BTreeSzie(root);
	printf("BTreeSize:%d\n", size);
	size = 0;
	BTreeSzie(root);
	printf("BTreeSize:%d\n", size);
	size = 0;
	BTreeSzie(root);
	printf("BTreeSize:%d\n", size);*/

	printf("BTreeSize:%d\n", BTreeSzie(root));
	return 0;
}

😽总结

【数据结构】---几分钟简单几步学会手撕链式二叉树(上)
😽Ending,今天的链式二叉树的内容就到此结束啦~,如果后续想了解更多,就请关注我吧,一键三连哦 ~文章来源地址https://www.toymoban.com/news/detail-466127.html

到了这里,关于【数据结构】---几分钟简单几步学会手撕链式二叉树(上)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 数据结构之——(手撕)顺序表

    数据结构之——(手撕)顺序表

    本章会介绍的知识点如下图:                    顺序表的结构:逻辑结构与物理结构都是内存中一块连续开辟的空间,都是11对应的线性结构。 两种定义顺序表的方式代码如下         静态的顺序表         动态的顺序表:          在这两种结构中通常我们是会

    2024年02月11日
    浏览(15)
  • 【数据结构】手撕顺序表

    【数据结构】手撕顺序表

    顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储; 在数组上完成数据的增删查改。  1,  静态顺序表 :使用 定长数组 存储元素。 2., 动态顺序表 :使用 动态开辟 的数组存储。   静态顺序表只适用于确定知道需要存多少数

    2024年02月11日
    浏览(12)
  • 手撕数据结构之栈+例题

    手撕数据结构之栈+例题

    目录 一、栈的概念及结构 二、栈的头文件及基本框架 三、接口实现 1、对栈的初始化  2、栈的销毁 3、入栈操作 4、出栈操作  5、判断栈是否为空 6、返回栈顶元素 7、遍历栈 四、有效的括号 - 力扣(LeetCode) 题目描述:  思路: 代码: 栈:一种特殊的线性表,其只允许

    2024年02月13日
    浏览(13)
  • 【数据结构】手撕双向链表

    【数据结构】手撕双向链表

    目录 前言 1. 双向链表  带头双向循环链表的结构 2. 链表的实现 2.1 初始化 2.2 尾插 2.3 尾删 2.4 头插 2.5 头删 2.6 在pos位置之前插入 2.7 删除pos位置 3.双向链表完整源码 List.h List.c 在上一期中我们介绍了单链表,也做了一些练习题,在一些题中使用单链表会十分繁琐。因为单链

    2024年02月05日
    浏览(16)
  • 【手撕数据结构】二分查找(好多细节)

    🌈键盘敲烂,年薪30万🌈 目录 普通版本的二分查找: right只负责控制边界(少了两次比较): 时间复杂度更稳定的版本: BSLeftmost: BSRightmost:   🏸细节1:循环判定条件是left = right ⭐细节2:mid = (left + right ) 1 原因见代码注释 改动1:while条件是left right 改动2:right = nums.len

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

    数据结构:手撕图解双向循环链表

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

    2024年02月15日
    浏览(13)
  • 【数据结构】手撕归并排序(含非递归)

    【数据结构】手撕归并排序(含非递归)

    目录 一,归并排序(递归) 1,基本思想  2,思路实现 二,归并排序(非递归) 1,思路实现 2,归并排序的特性总结: 1,基本思想 归并排序 (MERGE-SORT) 是建立在 归并操作 上的一种 有效的排序算法 ,该算法是采用 分治法(Divide and Conquer) 的一个非常典型的应用; 将 已

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

    数据结构---手撕图解双向循环链表

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

    2024年02月15日
    浏览(11)
  • 【数据结构】手撕排序NO.1----排序初识

    【数据结构】手撕排序NO.1----排序初识

    目录  一. 前言 二. 排序的概念及运用         2.1 排序的概念         2.2 排序的运用         2.3 常见的排序算法 三. 冒泡and选择排序         3.1 冒泡排序         3.2 选择排序 四. 各大排序算法的复杂度和稳定性        从本期开始,我们的数据结构将迎来一个新的

    2024年02月16日
    浏览(13)
  • [数据结构 -- 手撕排序第三篇] 冒泡排序

    [数据结构 -- 手撕排序第三篇] 冒泡排序

    目录 1、常见的排序算法 1.1 交换排序基本思想 2、冒泡排序的实现 2.1 基本思想 2.2 单趟排序 2.2.1 单趟排序分析 2.2.2 单趟排序实现代码 3、冒泡排序完整代码实现 3.1 思路分析 3.2 代码实现 4、时间复杂度 5、优化算法 5.1 优化算法思路 5.2 优化算法代码实现 6、冒泡排序的特性总

    2024年02月13日
    浏览(12)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包