【数据结构和算法】--- 二叉树(3)--二叉树链式结构的实现(1)

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

一、二叉树的创建(伪)

在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,且为了方便后面的介绍,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。
基于二叉树的链式结构,于是可以先malloc动态开辟出二叉树的每个节点并初始化,然后通过节点中的指针struct BinaryTreeNode* left(指向左树)和struct BinaryTreeNode* right(指向右树),将各个节点连接起来,最后大致模拟出了一棵二叉树,代码如下:

typedef int BTDataType;
typedef struct BinaryTreeNode
{
    BTDataType data;
    struct BinaryTreeNode* left;  //左树
    struct BinaryTreeNode* right; //右树
}BTNode;
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;
}

在实现二叉树基本操作前,再回顾下二叉树的概念,一棵二叉树是结点的一个有限集合,该集合:

  • 或者为空
  • 由一个根节点加上两棵分别称为左子树和右子树的二叉树组成

二叉树满足的条件:

  • 二叉树不存在度大于2的结点
  • 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树。

其余二叉树的概念还请回顾:【数据结构和算法】—二叉树(1)–树概念及结构
【数据结构和算法】--- 二叉树(3)--二叉树链式结构的实现(1),数据结构和算法,数据结构,算法
从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。


注意: 上述代码并不是创建二叉树的方式,真正创建二叉树方式将在后面介绍。

二、二叉树的遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。 访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。下图可以理解为是二叉树的前序遍历:
【数据结构和算法】--- 二叉树(3)--二叉树链式结构的实现(1),数据结构和算法,数据结构,算法
按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:

  1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
  2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
  3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。 NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。

2.1 前序遍历

前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。 依据此规律我们便可如下遍历二叉树,为了方便观察,每次遍历到根节点都输出一下:

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		putchar('N');
		return;
	}
	putchar(root->val);  //访问根节点
	BinaryTreePrevOrder(root->left);  //访问左子树
	BinaryTreePrevOrder(root->right);  //访问右子树
}

前序遍历代码,逻辑结构大致如下图,可以参考一下:
【数据结构和算法】--- 二叉树(3)--二叉树链式结构的实现(1),数据结构和算法,数据结构,算法
在这利用递归思想来解决前序遍历的问题,因为是前序遍历(访问顺序依次是根节点,左子树,右子树),所以在进入下层递归前可以先输出根节点。当进入左/右子树时,会更新根节点(原为上层根节点的左/右孩子节点) 。二叉树的叶子节点的左右孩子都为NULL,那么便可将递归的结束条件定为NULL。这便是前序遍历的递归逻辑。

2.2 中序遍历

中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。 与前序遍历相似,只是访问顺序不同(依次是左子树,根节点,右子树),那么调整一下代码顺序即可,将输出根节点值的操作(putchar(root->val);),放在访问左子树之后。那么递归每次都会先进入左子树,且最先打印的为叶子节点。代码如下:

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		putchar('N');
		return;
	}
	BinaryTreeInOrder(root->left);  //访问左子树
	putchar(root->val);  //输出根节点
	BinaryTreeInOrder(root->right);  //访问右子树
}

2.3 后序遍历

后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。 同样与前序遍历相似,访问顺序不同(依次是左子树,右子树,根节点),依此特性所以我们只需将输出操作(putchar(root->val))放到最后,其余代码不变。实现思想-递归完左子树和右子树后再输出根节点。 代码如下:

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{

	if (root == NULL)
	{
		putchar('N');
		return;
	}
	BinaryTreePostOrder(root->left);  //访问左子树
	BinaryTreePostOrder(root->right);  //访问右子树
	putchar(root->val);  //输出根节点
}

三种遍历最后输出的结果(图中N表示递归时遇到了NULL):

  • 前序遍历结果:1 2 3 4 5 6
  • 中序遍历结果:3 2 1 5 4 6
  • 后序遍历结果:3 2 5 6 4 1

【数据结构和算法】--- 二叉树(3)--二叉树链式结构的实现(1),数据结构和算法,数据结构,算法


  1. 设一课二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为 ( D )。
    A adbce
    B decab
    C debac
    D abcde

解: 解题思想(给定两种遍历序列,求出另外一种):我们可以根据各种遍历方法的特性来求解。( 1 ) 根据后续遍历序列找出根节点,因为后续遍历最后才会输出根,那么在序列的最后一个即为根节点a;( 2 ) 接着在中序遍历序列中找出根节点,然后划分左子树和右子树;( 3 ) 然后再到后序遍历序列中去除左子树和根节点,那么得到的便是右子树,并将其看为独立的树;( 4 ) 重复上述三步操作,直到排出完整的树。
图解如下:
【数据结构和算法】--- 二叉树(3)--二叉树链式结构的实现(1),数据结构和算法,数据结构,算法
解决此类问题最重要的还是要弄懂代码的递归思想。

三、二叉树节点个数及高度

3.1 二叉树节点个数

求二叉树的节点个数,利用的还是递归思想。我们可以将问题转化为----根节点(1),左子树的节点个数(root->left)和右子树的节点个数(root->right)的总结点个数。我们可以将根节点为空(root == NULL)作为递归结束的条件,并返回0(return 0)。 这种方法通常被称为递归分治

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

代码图解:
【数据结构和算法】--- 二叉树(3)--二叉树链式结构的实现(1),数据结构和算法,数据结构,算法

3.2 二叉树叶子节点个数

叶子节点概念:含有的子树个数为0的节点。 如本次创建的二叉树的节点3,节点5,节点6。
基于叶子节点的特性,同样可以利用递归分治的方法,将问题同化为----左子树的叶子节点个数和右子树的叶子节点个数之和

函数返回的条件:

  • 当前节点(root)的左子树(root->left)和右子树(root->right)都为空时(即!(root->left && root->right)),那么此节点为叶子节点,并返回1
  • 当前节点为空节点(即(root == NULL)),返回0
  • 函数执行到最后,返回当前树的叶子节点数。
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (!(root->left && root->right))
		return 1;
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

代码图解:
【数据结构和算法】--- 二叉树(3)--二叉树链式结构的实现(1),数据结构和算法,数据结构,算法

3.3二叉树第k层节点个数

既然是求第k层的节点个数,那我们便可以定义一个变量k,记录当前函数所要递归的层数。既然k的值是变化的,那么可以将他作为函数的参数,每递归一层便让他减一k - 1,那么k的值到1时,便是我们所要求的二叉树的第k层。

依据上述关系,便可以得出 函数返回的条件:

  • 遇到空节点时(root == NULL),返回0
  • k == 1时(说明到了二叉树的第k层),且当前节点不为空(root != NULL),那么便可返回1
  • 函数执行到了最后,返回统计到的符合条件的节点个数。
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k == 1 && root != NULL)
		return 1;
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

根据函数返回条件不难发现,k == 1时递归便不会继续往下层执行,这是因为此时函数必定会满足两个if条件中的一个,这也避免了递归到二叉树的第k + 1层。

代码图解:
【数据结构和算法】--- 二叉树(3)--二叉树链式结构的实现(1),数据结构和算法,数据结构,算法

3.4 二叉树查找值为x的节点

查找值为x的节点,可以将递归分治为----判断当前节点,判断左子树,判断右子树。 那么当遇到空节点(root == NULL)就返回return NULL;如果遇到所要查找的值(root->val == x)就返回当前地址(return root);那么如果都不满足就继续搜寻左子树,然后右子树;直到最后搜寻完整棵二叉树,都没有找到x,那么便返回NULL
还需要注意的一个问题是,如果在递归过程中找到了目标值x返回了当前地址root,但是现在只是回到了上一层递归的地方,返回值并不会被接收,而是继续执行下一个逻辑。 那么我们便可以用BTNode* ret1来接受函数的返回值,并判断,当返回值(ret1)不为NULL时(即说明上一次递归时,找到了x)直接返回此值return ret1

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
    //空节点
	if (root == NULL)
		return NULL;
	//值为x的节点
	if (root->val == x)
		return root;
	//左子树递归,ret接收返回值,并判断
	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1 != NULL)
		return ret1;
	//方法一:易于理解
	//BTNode* ret2 = BinaryTreeFind(root->right, x);
	//if (ret2 != NULL)
	//	return ret2;
	return BinaryTreeFind(root->right, x);
	return NULL;
}

代码图解:
【数据结构和算法】--- 二叉树(3)--二叉树链式结构的实现(1),数据结构和算法,数据结构,算法

四、二叉树的创建(真)

通过上面对各种遍历方法和递归思想的讲解,相信使用递归来真正创建二叉树也不难了,如下:

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
    //判断是否为空,即当a[*pi] == '#'时
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
	//动态开辟节点
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc()::fail");
		exit(-1);
	}//赋值并连接(递归)
	node->val = a[*pi];
	(*pi)++;
	node->left = BinaryTreeCreate(a, pi);
	node->right = BinaryTreeCreate(a, pi);
	retur

上面介绍的二叉树的一些函数的执行结果如下:

【数据结构和算法】--- 二叉树(3)--二叉树链式结构的实现(1),数据结构和算法,数据结构,算法
另外还有一些较为复杂的函数将在下一篇文章中介绍。文章来源地址https://www.toymoban.com/news/detail-822371.html

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

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

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

相关文章

  • 数据结构:二叉树的链式结构的实现

      目录  1.通过前序遍历构建二叉树 2. 二叉树的销毁  3.二叉树的遍历 4. 二叉树的节点个位和二叉树的叶子节点个位数 5. 二叉树的的k层节点数和查找值为x的节点 6. 判断二叉树是否为完全二叉树和求二叉树的高度h 二叉树的前序遍历 二叉树的中序遍历 二叉树的后序遍历

    2024年02月12日
    浏览(35)
  • 【数据结构】二叉树链式结构的实现(三)

    目录 一,二叉树的链式结构 二,二叉链的接口实现         1,二叉链的创建         2,接口函数         3,动态创立新结点         4,创建二叉树         5,前序遍历         6,中序遍历         7,后序遍历 三,结点个数以及高度等      

    2024年02月08日
    浏览(29)
  • 【数据结构】二叉树的链式结构的实现 -- 详解

    在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习。 注意 :上述代码并不是创建二叉树的方式。 学习二叉树结构,最简单的方式就是遍历。所谓

    2024年02月12日
    浏览(33)
  • 数据结构(C语言实现)——二叉树的概念及二叉树顺序结构和链式结构的实现(堆排序+TOP-K问题+链式二叉树相关操作)

    前面学习了数据结构中线性结构的几种结构,顺序表,链表,栈和队列等,今天我们来学习一种非线性的数据结构——树。由于二叉树是数据结构中的一个重点和难点,所以本文着重介绍二叉树的相关概念和性质,以及二叉树的应用。 树是一种非线性的数据结构,它是由n(

    2023年04月21日
    浏览(37)
  • 模拟实现链式二叉树及其结构学习——【数据结构】

    W...Y的主页 😊 代码仓库分享 💕 之前我们实现了用顺序表完成二叉树(也就是堆),顺序二叉树的实际作用就是解决堆排序以及Topk问题。 今天我们要学习的内容是链式二叉树,并且实现链式二叉树,这篇博客与递归息息相关! 目录 链式存储 二叉树链式结构的实现 链式二叉

    2024年02月07日
    浏览(33)
  • 【数据结构】二叉树链式结构的实现及其常见操作

    目录 1.手搓二叉树 2.二叉树的遍历 2.1前序、中序以及后序遍历 2.2二叉树的层序遍历 3.二叉树的常见操作 3.1求二叉树节点数量 3.2求二叉树叶子节点数量 3.3求二叉树第k层节点个数 3.3求二叉树的深度 3.4二叉树查找值为x的节点 4.二叉树的销毁 在学习二叉树的基本操作前,需先要

    2024年02月12日
    浏览(39)
  • 【数据结构】二叉树的链式实现及遍历

    后文所有代码中的二叉树结点: 前,中,后序遍历都可以采用分治递归的思想解决,将根节点和它的孩子结点分别处理。 此处仅利用递归展开图分析前序遍历,中序和后序也是相同的思想: 层序遍历需要利用队列来进行,如果二叉树跟结点不为空,则让 指向它的一个指针入

    2024年02月07日
    浏览(41)
  • 数据结构——二叉树的链式存储的实现

                             InitBiTree(BiTree T)——初始化二叉树。                         CreateBiTree(BiTree T)——先序遍历的顺序建立二叉链表。                         PreOrderTraverse(BiTree T)——先序遍历。                         

    2024年02月04日
    浏览(45)
  • 数据结构入门(C语言版)二叉树链式结构的实现

    简单回顾一下二叉树的 概念: ★ 空树 ★非空:根节点,根节点的左子树、根节点的右子树组成的。 从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。 下面我们先看二叉树的结构体定义以及创建 首先结构体的定义是元素本身,以

    2023年04月23日
    浏览(40)
  • 数据结构第六课 -----链式二叉树的实现

    🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 ​🎂 作者介绍: 🎂🎂 🎂 🎉🎉🎉🎉🎉🎉🎉 🎂 🎂作者id:老秦包你会, 🎂 简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂 喜欢学习C语言和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂

    2024年02月03日
    浏览(42)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包