二叉树实现

1.堆和二叉树的区别

(1)堆一定是完全二叉树

(2).堆的父节点一定比子节点要大(小),而普通二叉树没有这种要求

2.不同遍历方法

我们二叉树主要遍历方式分为

前序遍历,后序遍历,中序遍历和层序遍历

前序遍历:根->左子树->右子树

后序遍历:左子树->根->右子树

中序遍历:右子树->根左子数

层序遍历 :一层一层去遍历

为了方便理解,我这个地方默认每一个数都有左右节点,没有数据的节点为空

3,前序遍历打印的实现

这个时候二叉树就不能用堆的方法,还是以上图为例子,像上图这种方式怎么实现物理结构和逻辑结构的统一啊?

你会发现这个图你画不出来,堆有严格的要求,而二叉树却不一定是完全二叉树去啊?

那这个地方最好的处理方式就是递归?为啥是递归?

你可以将每个节点都理解成一棵树,每个树有两个子节点(没有值节点就为NULL)

而这两个节点 每个节点 都有 两个子节点,而这两个子节点 每个也有 两个子节点,

这就像俄罗斯套娃一样,像这种结构的动作,我们想到了什么?当然是递归啊!

那我们就来实现一下上面的那个二叉树

这个地方我们一些简单的节点就直接跳过

tree.h

cpp 复制代码
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

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

//申请节点
BTNode* BuyNode(BTDatatype x);
//前序遍历打印
void PreOrder(BTNode* root);
//创造二叉树
BTNode* CreatTree();

手动申请节点

cpp 复制代码
//申请节点
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;
}

例子的二叉树实现

cpp 复制代码
BTNode* CreatTree()
{
	BTNode* nodel1 = BuyNode(1);
	BTNode* nodel2 = BuyNode(3);
	BTNode* nodel3 = BuyNode(9);
	BTNode* nodel4 = BuyNode(10);
	BTNode* nodel5 = BuyNode(2);
	BTNode* nodel6 = BuyNode(12);
	BTNode* nodel7 = BuyNode(16);
	BTNode* nodel8 = BuyNode(17);
	nodel1->left = nodel2;
	nodel1->right = nodel6;
	nodel2->left = nodel3;
	nodel2->right = nodel4;
	nodel4->left = nodel5;
	nodel6->right = nodel7;
	nodel7->right = nodel8;
	return nodel1;
}

这个地方重点怎么遍历打印?

先思考,什么时候打印不进行下去?

递归非常重要的一点就是我们需要一个停下来的条件

停下来的条件就是再往后没有数据了,也就是NULL,这就是这个地方BuyNode那个函数要

置为NULL的原因之一啊!

然后递归就很好写了

我们先是前序打印,那就是先根,再左节点最后右节点

那我们这个地方我们就可以实现递归了

cpp 复制代码
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL\n");
		return;
	}
	printf("%d\n", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

光看代码优点难受,那这个地方我们画一个图

下面有两个图,哪个图相对更符合上面二叉树的代码的

图1

图2

显然答案是1,为啥图2不行呢?

第一我们这个地方使用的都是指针,而不是结构体的递归,并且结构体不能递归

为什么?结构体一旦递归,它内存怎么算,同一个类型的结构体的内存大小不一样会很麻烦

传指针不仅我传的变量更小,而且效率更高,指针的大小和类型无关!这样可以保证同一类型的结构体内存大小相同

我们用前序递归来打印一下,看上面写的对不对

很显然没有问题,那么如果我要 中序遍历打印 后序遍历打印 或者层序遍历打印 怎么打印呢?

中序遍历打印 和 后序遍历打印 就直接把 前序打印的这三个 交换一下顺序

printf("%d\n", root->data); //根的打印

PreOrder(root->left); //打印左子树

PreOrder(root->right); //打印右子树

至于遍历打印,后面会单独实现

4.二叉树元素个数

这个地方我们要思考一下怎么去计算二叉树元素个数

方案1:我们首先想到的是函数传参多传一个参数size,每遍历一个size就++

但是这个地方显然有问题就是我们传的是实参,实参的改变不改变形参

而且我们这个地方递归,每一次函数的调用的size不是同一个size

方案2:用static,答案也不行,static修饰的静态变量只能初始化一次,我每一次函数调用都初始化一次,这显然矛盾,而且static修饰的静态变量出了函数无法使用,后续无法更改!

方案3:既然这些都不行,那我们就简单点嘛!我们传指针,但是这个地方也不是最优解,因为我每次调用一次这个函数就得重新创建一个变量和它的地址

我们接下来再思考一下

这个地方其实我们适合用斐波那契数列去类比理解

二叉树和斐波那契数列没有关系,只是类比为了方便理解

这本质上还是递归的思想

那这就很简单了

我们直接操作返回值就可以了

如果是NULL返回值就是0

然后返回值直接返回 左树的返回值+右树的返回值+1(这个1是节点,节点也要算进去)

那就很容易写代码啦

cpp 复制代码
//二叉树元素个数
int TreeSize(BTNode* root)
{
	return root == NULL ? 0 :
		TreeSize(root->left)
		+ TreeSize(root->right)
		+1;
}
5.求树的深度

这个地方也可以用斐波那契数列的思想嘛!

那我们先来看一段代码

cpp 复制代码
//二叉树的深度
int TreeHeight(BTNode*root)
{
	if (root == NULL)
		return 0;
	return TreeHeight(root->left) > TreeHeight(root->right) 
		? TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;
}

这样写代码可不可以?

答案是不可以啊!你这个地方return是调用函数获得返回值,

TreeHeight(root->left) > TreeHeight(root->right)

这个地方就调用了两遍函数,

? TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;

还要再调用一边函数,函数的重复调用是很恐怖的这里,这里是递归啊,我多每一次调用都要调用函数好多次,直到递归停止啊!

那我们怎么办?

返回值得到就保存起来呗!

没必要每次要用返回值只能通过调用函数得到啊!

cpp 复制代码
//二叉树的深度
int TreeHeight(BTNode*root)
{
	if (root == NULL)
		return 0;
	int LeftHeight = TreeHeight(root->left);
	int RightHeight = TreeHeight(root->left);
	return LeftHeight > RightHeight
		? LeftHeight : RightHeight;
}

5.树的第k层元素个数

这个地方其实很简单啊!

还是递归方法

树的第k层元素个数

但是这个要注意,我们停下来的条件是啥?

第一是到第k层了

第二就是遇到NULL 返回0;

返回0就算不是第k层遇到,加上0也不会影响第k层元素计算结果

cpp 复制代码
//求第k层节点个数
int TreeLevel(BTNode* root, int k)
{
	assert(k > 0);
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return TreeLevel(root->left, k - 1) 
		+ TreeLevel(root->right, k - 1);
}
相关推荐
胡西风_foxww14 分钟前
【es6复习笔记】rest参数(7)
前端·笔记·es6·参数·rest
2401_858286113 小时前
115.【C语言】数据结构之排序(希尔排序)
c语言·开发语言·数据结构·算法·排序算法
胡西风_foxww3 小时前
【es6复习笔记】函数参数的默认值(6)
javascript·笔记·es6·参数·函数·默认值
猫猫的小茶馆3 小时前
【数据结构】数据结构整体大纲
linux·数据结构·算法·ubuntu·嵌入式软件
胡西风_foxww3 小时前
【es6复习笔记】生成器(11)
javascript·笔记·es6·实例·生成器·函数·gen
waterme1onY4 小时前
Spring AOP 中记录日志
java·开发语言·笔记·后端
2401_879103684 小时前
24.12.25 AOP
java·开发语言·笔记
2401_858286115 小时前
109.【C语言】数据结构之求二叉树的高度
c语言·开发语言·数据结构·算法
huapiaoy5 小时前
数据结构---Map&Set
数据结构
南宫生5 小时前
力扣-数据结构-1【算法学习day.72】
java·数据结构·学习·算法·leetcode