【数据结构】二叉树的实现

如有不懂的地方,可翻阅我之前文章哦!

个人主页小八哥向前冲~

所属专栏数据结构【c语言】

目录

前言

二叉树的遍历

前序遍历

中序遍历

后序遍历

总结点数

二叉树的高度

第k层叶子数

查找x值

叶子总数

左(右)孩子数

树的销毁

总代码


前言

前一章我们学习了堆,并且了解了什么是树。简单来说,堆就是一个二叉树,现在我们来真正了解一下二叉树!

以这棵树为例:

我们如何用链式结构来表示一颗二叉树呢?不错,结构体

根据二叉树的节点特点,可以将每个部分分为左孩子节点,右孩子节点和根节点,于是我们可以这样来描述它:

cpp 复制代码
typedef struct TreeNode
{
	struct TreeNode* left;//左孩子节点
	struct TreeNode* right;//右孩子节点
	TDatatype val;//节点数值
}TNode;

现在我们手搓一个二叉树(上图为例),来进行深入研究!

cpp 复制代码
//创建节点
TNode* BuyNode(TDatatype x)
{
	TNode* node = (TNode*)malloc(sizeof(TNode));
	if (node == NULL)
	{
		perror("malloc failed!");
		return NULL;
	}
	node->left = node->right = NULL;
	node->val = x;
	return node;
}
//手动创建一个二叉树
TNode* CreateTree()
{
	TNode* node1 = BuyNode(1);
	TNode* node2 = BuyNode(2);
	TNode* node3 = BuyNode(3);
	TNode* node4 = BuyNode(4);
	TNode* node5 = BuyNode(5);
	TNode* node6 = BuyNode(6);

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

	return node1;
}

二叉树的遍历

我们知道链表如何遍历,那么一颗链式树是怎么遍历的呢?

这里有三种遍历方法------前,中,后序遍历!

前序遍历

前序遍历是按照:根,左子树,右子树的方式层层遍历的

(上图为例)前序遍历结果为:1 2 3 NULL NULL NULL 4 5 NULL NULL 6 NULL NULL。

代码:

cpp 复制代码
//前序遍历  根 左子树 右子树
void Preoder(TNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%d ", root->val);
	Preoder(root->left);
	Preoder(root->right);
}

当根走完,再走左子树遍历,再走右子树遍历,直到全部走完!(递归方式走完)

中序遍历

中序遍历按照:左子树,根,右子树的的方式遍历

(上图为例)中序遍历结果:NULL 3 NULL 2 NULL 1 NULL 5 NULL 4 NULL 6 NULL 。

代码:

cpp 复制代码
//中序遍历  左子树 根 右子树
void Inoder(TNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	Inoder(root->left);
	printf("%d ", root->val);
	Inoder(root->right);
}

先走左子树遍历,再走根遍历,再走右子树遍历,直到全部递归走完!

后序遍历

后序遍历按照:左子树,右子树,根方式遍历。

(上图为例)后序遍历结果:NULL NULL 3 NULL 2 NULL NULL 5 NULL NULL 6 4 1。

代码:

cpp 复制代码
//后序遍历  左子树 右子树 根
void Afteroder(TNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	Afteroder(root->left);
	Afteroder(root->right);
	printf("%d ", root->val);
}

先走左子树遍历,再走右子树遍历,最后走根!直到递归走完!

总结点数

我们不难知道:总结点数==左子树节点数+右子树节点数+1(根本身)。

如果根为空,节点数就是0。

代码

cpp 复制代码
//节点数
int TreeSize(TNode* root)
{
	//每个部分可以看成:左子树+右子树+1(自身)
	return root == NULL ? 0 : TreeSize(root->left) +
		TreeSize(root->right) + 1;
}

二叉树的高度

分析 :树的高度==左右子树中高的那个子树的高度+1。

代码

cpp 复制代码
int HeightTree(TNode* root)
{
	//高的子树+1就是高度
	if (root == NULL)
		return 0;
	int leftheight = HeightTree(root->left);
	int rightHeight = HeightTree(root->right);
	return leftheight > rightHeight ?
		leftheight + 1 : rightHeight + 1;
}

第k层叶子数

分析:第k层叶子数==第k层的左子树的叶子数+第k层的右子树的叶子数。

如果第k层的叶子为空,那就没有这个叶子!

代码

cpp 复制代码
//第k层树的节点数 
int LeafKSize(TNode* root, int k)
{
	//第k层:左子树+右子树的叶子数
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return LeafKSize(root->left, k - 1) + LeafKSize(root->right, k - 1);
}

查找x值

思路:先找左子树,如果有返回,没有再去右子树查找,有的话返回,没有返回空。

代码

cpp 复制代码
//查找值为x的节点
TNode* TreeFind(TNode* root, TDatatype x)
{
	if (root == NULL)
		return NULL;
	if (root->val == x)
		return root;
	TNode* node1 = TreeFind(root->left,x);
	//左子树没找到,就去右子树找
	if (node1)
		return node1;
	//在右子树找
	return TreeFind(root->right, x);
}

叶子总数

思路 :叶子总数==左子树叶子总数+右子树叶子总数。

我们要判断某个节点是不是叶子,这个节点的左右孩子是否都为NULL就行!

代码

cpp 复制代码
//叶子数
int LeafSize(TNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL)
		return 1;
	return LeafSize(root->left) + LeafSize(root->right);
}

左(右)孩子数

以统计左孩子数为例:

我们知道:左孩子数==左子树中的左孩子数+右子树中的左孩子数

思路

遍历左子树和右子树,如果有左孩子就记下来!

代码

cpp 复制代码
//左孩子数
int LeftSize(TNode* root)
{
	int count = 0;
	if (root == NULL)
		return 0;
	else
	{
		//如果有左孩子,数量就存起来
		if (root->left)
			count++;
	}
	//遍历一遍,最后再加总数
	return LeftSize(root->left) + LeftSize(root->right) + count;
}

树的销毁

销毁树节点要注意一个点就是:需要先销毁左子树节点,再销毁右子树节点,最后销毁根节点!

代码

cpp 复制代码
//二叉树的销毁
void TreeDestroy(TNode* root)
{
	if (root == NULL)
		return;
	TreeDestroy(root->left);
	TreeDestroy(root->right);
	free(root);
}

总代码

Tree.h文件

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


typedef int TDatatype;
typedef struct TreeNode
{
	struct TreeNode* left;
	struct TreeNode* right;
	TDatatype val;
}TNode;

//创建节点
TNode* BuyNode(TDatatype x);
//创建一个二叉树
TNode* CreateTree();
//前序遍历   根 左子树 右子树
void Preoder(TNode* root);
//中序遍历   左子树 根 右子树
void Inoder(TNode* root);
//后序遍历   左子树 右子树 根
void Afteroder(TNode* root);
//节点数
int TreeSize(TNode* root);
//叶子数
int LeafSize(TNode* root);
//第k层树的节点数 
int LeafKSize(TNode* root,int k);
//查找值为x的节点
TNode* TreeFind(TNode* root, TDatatype x);
//二叉树的销毁
void TreeDestroy(TNode* root);
//二叉树的高度
int HeightTree(TNode* root);
//左孩子数
int LeftSize(TNode* root);

Tree.c文件

cpp 复制代码
//创建节点
TNode* BuyNode(TDatatype x)
{
	TNode* node = (TNode*)malloc(sizeof(TNode));
	if (node == NULL)
	{
		perror("malloc failed!");
		return NULL;
	}
	node->val = x;
	node->left = node->right = NULL;
	return node;
}
//创建一个二叉树
TNode* CreateTree()
{
	TNode* node1 = BuyNode(1);
	TNode* node2 = BuyNode(2);
	TNode* node3 = BuyNode(3);
	TNode* node4 = BuyNode(4);
	TNode* node5 = BuyNode(5);
	TNode* node6 = BuyNode(6);

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

	return node1;
}
//前序遍历   根 左子树 右子树
void Preoder(TNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%d ", root->val);
	Preoder(root->left);
	Preoder(root->right);
}
//中序遍历   左子树 根 右子树
void Inoder(TNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	Inoder(root->left);
	printf("%d ", root->val);
	Inoder(root->right);
}
//后序遍历   左子树 右子树 根
void Afteroder(TNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	Afteroder(root->left);
	Afteroder(root->right);
	printf("%d ", root->val);
}
//节点数
int TreeSize(TNode* root)
{
	//每个部分可以看成:左子树+右子树+1(自身)
	return root == NULL ? 0 : TreeSize(root->left) +
		TreeSize(root->right) + 1;
}
//叶子数
int LeafSize(TNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL)
		return 1;
	return LeafSize(root->left) + LeafSize(root->right);
}
//第k层树的节点数 
int LeafKSize(TNode* root, int k)
{
	//第k层:左子树+右子树的叶子数
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return LeafKSize(root->left, k - 1) + LeafKSize(root->right, k - 1);
}
//查找值为x的节点
TNode* TreeFind(TNode* root, TDatatype x)
{
	if (root == NULL)
		return NULL;
	if (root->val == x)
		return root;
	TNode* node1 = TreeFind(root->left,x);
	//左子树没找到,就去右子树找
	if (node1)
		return node1;
	//在右子树找
	return TreeFind(root->right, x);
}
//二叉树的销毁
void TreeDestroy(TNode* root)
{
	if (root == NULL)
		return;
	TreeDestroy(root->left);
	TreeDestroy(root->right);
	free(root);
}
//二叉树的高度
int HeightTree(TNode* root)
{
	//高的子树+1就是高度
	if (root == NULL)
		return 0;
	int leftheight = HeightTree(root->left);
	int rightHeight = HeightTree(root->right);
	return leftheight > rightHeight ?
		leftheight + 1 : rightHeight + 1;
}
//左孩子数
int LeftSize(TNode* root)
{
	int count = 0;
	if (root == NULL)
		return 0;
	else
	{
		//如果有左孩子,数量就存起来
		if (root->left)
			count++;
	}
	//遍历一遍,最后再加总数
	return LeftSize(root->left) + LeftSize(root->right) + count;
}

这期递归较多,比较难理解,有难度!

相关推荐
浅念同学3 小时前
算法-常见数据结构设计
java·数据结构·算法
Java资深爱好者3 小时前
如何在std::map中查找元素
开发语言·c++
UndefindX3 小时前
PAT甲级1006 :Sign In and Sign Out
数据结构·算法
杨和段5 小时前
简介空间复杂度
数据结构
Overboom6 小时前
[数据结构] --- 线性数据结构(数组/链表/栈/队列)
数据结构
T风呤6 小时前
学生管理系统(通过顺序表,获取连续堆区空间实现)
算法
安步当歌6 小时前
【FFmpeg】av_write_trailer函数
c语言·c++·ffmpeg·视频编解码·video-codec
stackY、7 小时前
【Linux】:程序地址空间
linux·算法
心死翼未伤7 小时前
【MySQL基础篇】多表查询
android·数据结构·数据库·mysql·算法
Orion嵌入式随想录8 小时前
算法训练 | 图论Part1 | 98.所有可达路径
算法·深度优先·图论