数据结构与算法之《二叉树》详解,大数据开发面试基础

49 阅读11分钟

作者:@Ggggggtm

寄语:与其忙着诉苦,不如低头赶路,奋路前行,终将遇到一番好风景

文章目录

一、树的概念及结构

二、二叉树的概念及结构

2、1 二叉树的概念

2、2 二叉树的特点

2、3 二叉树的结构(图片)

2、4 特殊的二叉树

三、二叉树的代码及思路实现

3、1 二叉树的存储结构

3、1、1 二叉树的顺序存储结构

3、1、2 二叉树的链式存储结构

3、2 二叉树链式结构的实现

3、2、1 定义结构体

3、2、2 自定义一个二叉树

3、2、3 前序遍历

3、2、4 中序遍历

3、2、5 后序遍历

3、2、6 求树中节点的个数

3、2、7 求树中叶节点的个数

3、3 二叉树的性质


一、树的概念及结构

  二叉树是树的一种,所以在学习二叉树之前我们先了解一下树的概念及结构。

  树是一种 非线性 的数据结构,它是由 n ( n>=0 )个有限节点组成一个具有层次关系的集合。 把它 叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的

  • 有一个特殊的结点,称为根结点,根节点没有前驱结点;

  • 除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以 有0个或多个后继;

  • 因此,树是递归定义的。

这里还有我们熟知的树中的一些概念:

  • 节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6;

  • 叶节点或终端节点:度为 0 的节点称为叶节点; 如上图: B 、 C 、 H 、 I... 等节点为叶节点;

  • 非终端节点或分支节点:度不为 0 的节点; 如上图: D 、 E 、 F 、 G... 等节点为分支节点;

  • 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图: A 是 B 的父节点;

  • 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图: B 是 A 的孩子节点 ;

  • 兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图: B 、 C 是兄弟节点;

  • 树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为 6;

  • 节点的层次:从根开始定义起,根为第 1 层,根的子节点为第 2 层,以此类推;

  • 树的高度或深度:树中节点的最大层次; 如上图:树的高度为 4;

  • 节点的祖先:从根到该节点所经分支上的所有节点;如上图: A 是所有节点的祖先;

  • 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是 A 的子孙;

  • 森林:由 m ( m>0 )棵互不相交的多颗树的集合称为森林;(数据结构中的学习并查集本质就是 一个森林);

  在这里我们要注意树与非树的区别是;

  • 子树是不相交的;

  • 除了根结点外,每个节点有且仅有一个父结点;

  • 一颗N个结点的树有N-1条边。

以下我给大家举几个树和非树的例子,可以先简单了解以下。

树:

非树:

二、二叉树的概念及结构

2、1 二叉树的概念

  一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。

2、2 二叉树的特点

  • 每个结点最多有两棵子树,即二叉树不存在度大于2的结点。

  • 二叉树的子树有左右之分,其子树的次序不能颠倒。

2、3 二叉树的结构(图片)

2、4 特殊的二叉树

  1. **满二叉树:**一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉 树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。

  2. **完全二叉树:**完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对 于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号 从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

三、二叉树的代码及思路实现

3、1 二叉树的存储结构

   二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构

3、1、1 二叉树的顺序存储结构

  顺序结构存储就是使用 数组来存储 ,一般使用数组 只适合表示完全二叉树 ,因为不是完全二叉树 会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

3、1、2 二叉树的链式存储结构

  二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成数据域左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链。

3、2 二叉树链式结构的实现

  二叉树的链式结构实现都有哪些模块呢?接下来我简单的给大家总结一下:

  1. 定义结构体;

  2. 自定义一个二叉树;

  3. 前序遍历;

  4. 中序遍历;

  5. 后序遍历;

  6. 求树中节点的个数;

  7. 求叶节点的个数。

接下来我们来看一下各个模块实现的细节以及详解。

3、2、1 定义结构体

   定义结构体时,由上面的链式存储结构我们直到该结构体应该包含一个存储数据的变量,和指向左右分支节点的指针;我们看代码实现。

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

3、2、2 自定义一个二叉树

  首先我们自己要有一个二叉树,简单的二叉树即可。因为下面的操作都是在二叉树上进行的。这里给出一个简单的二叉树,如下图及代码实现:

	BTNode* A = (BTNode*)malloc(sizeof(BTNode));
	A->data = 'A';
	A->left = NULL;
	A->right = NULL;

	BTNode* B = (BTNode*)malloc(sizeof(BTNode));
	B->data = 'B';
	B->left = NULL;
	B->right = NULL;

	BTNode* C = (BTNode*)malloc(sizeof(BTNode));
	C->data = 'C';
	C->left = NULL;
	C->right = NULL;

	BTNode* D = (BTNode*)malloc(sizeof(BTNode));
	D->data = 'D';
	D->left = NULL;
	D->right = NULL;

	BTNode* E = (BTNode*)malloc(sizeof(BTNode));
	E->data = 'E';
	E->left = NULL;
	E->right = NULL;
	
	A->left = B;
	A->right = C;
	B->left = D;
	B->right = E;

3、2、3 前序遍历

  什么是前序遍历呢?我们先来看一下比较官方的解释。

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

   我在稍微解释一下前序遍历的概念:其实就是遍历树时,先访问根,再访问左子树,最后访问右子树。这里要注意的是,当我们访问到左子树时,我们把左子树当成一个新树,同时也应该满足先访问根,在访问左子树,最后访问右子树。我们发现前序遍历先访问了整个树的跟后,再把整个树左子树访问完后,再从下往上依次访问整个数的右子树。

 其实我们不难发现,当一个子树的节点为空时,我们就不再往下访问了,开始从下往上访问右子树。这好像与递归有点类似哦!其实就是用递归实现的遍历。我们结合着下图理解一下:

 注:

  • 往下的箭头表示递归调用;

  • 往上的箭头表示返回,也就是归。

下面我们看代码的实现。

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

3、2、4 中序遍历

  什么是中序遍历呢?同样,我们先来看一下比较官方的解释。

   LNR:中序遍历 (Inorder Traversal)—— 访问根结点的操作发生在遍历其左右子树的中 间。

img img img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取