C C++最全《数据结构修炼手册》----链式二叉树的实现_鹿九丸(1),2024年最新2024年最新C C++大厂面试题来袭

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

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

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

2.2.1 前序遍历的实现

代码实现:

void PreOrder(BTNode*root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);//遍历根节点
	PreOrder(root->left);//遍历左子树节点
	PreOrder(root->right);//遍历右子树节点
}

图示:()中的数字为代码执行顺序

image-20220409200219445

2.2.2 中序遍历的实现

代码实现:

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

图示:

image-20220409210540857

2.2.3 后序遍历的实现

代码实现:

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

2.3 层序遍历(广度优先遍历(BFS))

层序遍历——直接一层一层的进行遍历即可,从头节点开始逐个向后进行遍历。上图中的遍历顺序为:1—2—4—3—5—6

思路:

  1. 先把根入队列,借助队列先进先出的性质
  2. 上一层的节点出的时候,带下一层的左右子节点进去

图示:

image-20220415154604840

实现:

注意:使用Queue.c和Queue.h两个文件,同时把队列中存储的数据类型定义为BinaryTreeNode*,注意头文件的包含问题和在Queue文件中的结构体的声明!

Queue.h文件中的修改:

image-20220415152436909

Test.c文件中的修改

image-20220415152615070

代码:

void LevelOrder(BTNode* root)
{
	Queue q;//队列的创建
	QueueInit(&q);
	if (root)//判断是否为空二叉树
	{
		QueuePush(&q, root);//将root节点push到队列中
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);//拿到队列首元素
		QueuePop(&q);//将出队列的节点从队列中删掉
		if (front->left)
		{
			QueuePush(&q, front->left);//将左子节点push到队列中
		}
		if (front->right)
		{
			QueuePush(&q, front->right);//将右子节点push到队列中
		}
		printf("%d ", front->data);//打印出队列的数据
	}
	//对列的销毁
	QueueDestory(&q);
}

2.4 层序遍历的应用----完全二叉树的判断

思路:

  1. 层序遍历,空节点也进队列。注意:在进队列之前,检查一下front是否为空,否则会出现对空指针进行解引用的错误。
  2. 出到空节点以后,出队列中所有的数据,如果全是空,就是完全二叉树,如果有非空,就不是。

图示:

image-20220415170651476

代码:

//判断一个二叉树是否是完全二叉树
bool BTreeComplete(BTNode* root)
{
	Queue q;//队列的创建
	QueueInit(&q);
	if (root)//判断是否为空二叉树
		QueuePush(&q, root);//将root节点push到队列中
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)//出到空的时候退出,后面再进行判断
		{
			break;
		}
		//为什么要放到后面push呢?为了防止对空指针进行解引用
		QueuePush(&q, front->left);//此时front一定不为空
		QueuePush(&q, front->right);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		//空后面出现非空,就说明不是完全二叉树
		if (front)
		{
			return false;
		}
		QueuePop(&q);
	}
    //队列的销毁
	QueueDestory(&q);
	return true;
}

注意:考虑下面这一种情况:

image-20220415172625693

image-20220415172901775

3. 节点个数以及高度等

3.1 二叉树节点个数

两种方法:

方法一

思路:遍历+计数

代码:

int count = 0;
void BTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	count++;
	BTreeSize(root->left);
	BTreeSize(root->right);
}

当然也可以用局部静态变量来存储节点个数但是不推荐,代码如下:

int BTreeSize(BTNode* root)
{
    static int count = 0;//只会在第一次进入时初始化一次
    if (root == NULL)
	{
		return count;
	}
	count++;
	BTreeSize(root->left);
	BTreeSize(root->right);
    return count;
}

当然,上面这两种方法都不好,都没有充分利用链式二叉树递归定义的结构性质,所以上面的两种方法都不推荐。上面的两种方法中第二种尤其不好,特别是在多次计算元素个数的时候,第二次计算如果count没有进行重新初始化操作将会仍然保留上一次计算的值,如果是用的全局变量的话还能每一次计数完之后重新进行赋值,但是第二种方法完全无法改变上一次计数后的残余值。

全局变量在进行多线程时会出现问题,比如在多线程的情况下就会出现问题:多个线程同时使用count这个全局变量,此时就会出现线程安全问题。

当然,也可以在调用函数中定义一个count变量,然后在调用的时候传入count的地址,那么BTreeSize函数必须这样进行定义:

int BTreeSize(BTNode* root,size_t *count)
{
    static int count = 0;//只会在第一次进入时初始化一次
    if (root == NULL)
	{
		return count;
	}
	(*count)++;
	BTreeSize(root->left);
	BTreeSize(root->right);
    return count;
}

方法二(推荐使用)

(递归法)

思路:子问题

1、空树,最小规模子问题,节点数返回0

2、非空,左子树节点个数 + 右子树节点个数 + 1(自己)

代码:

int BTreeSize(BTNode* root)
{
    return root==NULL ? 0 : 
    BTreeSize(root->left) + 
    BTreeSize(root->right) + 1;//1在最前面就是前序,在中间就是中序,在最后就是后序
}

这种方法充分利用了链式二叉树递归定义的结构性质,因为链式二叉树的本质就是由根节点和两个子树构成,求整个子树就是求根节点的节点数1加上左右子树的节点个数。

如图所示:

image-20220410105756117

注意:图中紫色数字代表返回值,黑色数字代表相应函数的返回的值。

运用的算法思想:分治

分治:把复杂的问题,分成更小规模的子问题,子问题再分成更小规模的子问题······直到子问题不可再分割,直接能得出结果。

3.2 二叉树叶子节点的个数

方法一

思路:(遍历+计数)

和上面的思路基本一致,只是在count++的前面加上一个叶子节点的判断条件,进而达到对叶子节计数的目的。

代码:

int count = 0;
void BTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == NULL&&root->right==NULL)
	{
		count++;
	}
	BTreeLeafSize(root->left);
	BTreeLeafSize(root->right);
}

方法二

思路:采用分治的思想。

代码:

int BTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == NULL&&root->right==NULL)
	{
		return 1;
	}
	else
		return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
}

当然,上面的代码可以简写为下面的代码:

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

img
img

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

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

如果你需要这些资料,可以戳这里获取

	1 + BTreeLeafSize(root->left)+ BTreeLeafSize(root->right) : 
	0 + BTreeLeafSize(root->left) + BTreeLeafSize(root->right);

}



[外链图片转存中...(img-iLh1OyA1-1715698777531)]
[外链图片转存中...(img-tLRAkZL7-1715698777531)]

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

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

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值