数据结构____二叉树初阶

一:二叉树的基本概念和性质

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

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

二叉树的性质:

  1. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有 个结点.

  2. 若规定根结点的层数为1,则深度为h的二叉树的最大结点数是 .

  3. 对任何一棵二叉树, 如果度为0其叶结点个数为 , 度为2的分支结点个数为 ,则有 = +1

  4. 若规定根结点的层数为1,具有n个结点的满二叉树的深度,h= . (ps: 是log以2

为底,n+1为对数)

  1. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从0开始编号,则对

于序号为i的结点有:

  1. 若i>0,i位置结点的双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点

  2. 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子

  3. 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子

二:二叉树一些接口的实现:

函数声明和头文件包含:

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

typedef char BTDataType;

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

// 申请节点
BTNode* BuyNode(BTDataType x);
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);
// 二叉树销毁
void BinaryTreeDestory(BTNode* root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 求二叉树的高度
int BinaryHeight(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root);

对应接口实现:

层序遍历的时候需要调用队列来实现。

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include "BTree.h"

// 申请节点
BTNode* BuyNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));

	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	else
	{
		newnode->data = x;
		newnode->left = NULL;
		newnode->right = NULL;
		return newnode;
	}
}

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N   ");

	}
	else
	{
		printf("%d  ", root->data);
		BinaryTreePrevOrder(root->left);
		BinaryTreePrevOrder(root->right);
	}
}

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N  ");
	}
	else
	{
		BinaryTreeInOrder(root->left);
		printf("%d  ", root->data);
		BinaryTreeInOrder(root->right);
	}
}

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N  ");
	}
	else
	{
		BinaryTreePostOrder(root->left);
		BinaryTreePostOrder(root->right);
		printf("%d  ", root->data);
	}
}

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	// 如果遍历到数组的 一个位置 当前位置为空
	if (a[*pi] == '#' || *pi >= n)
	{
		(*pi)++;
		return NULL;
	}

	BTNode* cur = BuyNode(a[*pi]);
	cur->data = a[*pi];
	(*pi)++;

	cur->left = BinaryTreeCreate(a, n, pi);
	cur->right = BinaryTreeCreate(a, n, pi);

	return cur;
}

// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
	// 采用后序遍历
	if (root == NULL)
	{
		return;
	}

	BinaryTreeDestory((root)->left);
	BinaryTreeDestory((root)->right);
	
	free(root);
	root = NULL;
}

// 层序遍历

// 上一层带下一层
void BinaryTreeLevelOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL");
		return;
	}
	Queue qu;
	QueueInit(&qu);

	// 如果树不是空树,先将第一个根节点入队列
	if (root != NULL)
	{
		QueuePush(&qu, root);
	}
	// 如果队列不为空,则将队列的中的队头数据取出,并pop掉,如果做右子树不为空,就将左右子树也入队列
	while(!QueueEmpty(&qu))
	{
		// 取队头元素,根节点
		BTNode* front = QueueFront(&qu);
		printf("%d  ", front->data);

		if(front->left)
			QueuePush(&qu, front->left);
		
		if(front->right)
			QueuePush(&qu, front->right);

		QueuePop(&qu);
	}
	QueueDestroy(&qu);
}

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	// 如果改树根节点不为空,则改树的节点个数等于左子树加右子树加1
	if (root == NULL)
	{
		return 0;
	}
	else
	{
		return (BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1);
	}
}

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	// 如果是空树
	if (root == NULL)
		return 0;

	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	//int left = BinaryTreeLeafSize(root->left);
	//int right = BinaryTreeLeafSize(root->right);

	//return left + right;

	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);

}

// 求二叉树的高度
int BinaryHeight(BTNode* root)
{
	// 求出左子树的高度和右子树的高度比较,大的那个加上根(+1);
	if (root == NULL)
	{
		return 0;
	}

	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}

	// 求出叶子节点的个数之后还要加上自己(根)才是最后的高度,左右子树都一样。
	int left = BinaryHeight(root->left)+1; 
	int right = BinaryHeight(root->right)+1;

	return left > right ? left : right;

	// 为什么这下面的代码和上面的结果不一样?
	//int left = BinaryHeight(root->left); 
	//int right = BinaryHeight(root->right);
	//return (left > right ? left : right + 1);
}

// 二叉树第k层节点个数
/*
求解第K层的节点个数,就是求解第K-1层左右节点的个数
*/
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	// 不管是多大的问题,都先要考虑特殊情况
	if (root == 0)
		return 0;

	// 使用K来当递归的出口
	if (k == 1)
	{
		return 1;
	}

	// 转换成子问题
	return BinaryTreeLevelKSize(root->right, k - 1)+ BinaryTreeLevelKSize(root->left, k - 1);
}

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}

	if (root->data == x)
	{
		return root;
	}
	
	// 这里找到了的值应该存起来,然后返回给上一层的函数调用
	// 自己写的这个代码如果右子树也为空的话就返回的是空了,但是也没关系,返回是空后面也可以,
	// 这个代码应该就是那个网图的那种情况
	//BTNode* Find = BinaryTreeFind(root->left, x);
	//if (Find != NULL)
	//{
	//	return Find;
	//}
	//else
	//{
	//	return Find = BinaryTreeFind(root->right, x);
	//}

	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)
	{
		return ret1;
	}

	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
	{
		return ret2;
	}

	return NULL;
}

// 判断二叉树是否是完全二叉树

bool BinaryTreeComplete(BTNode* root)
{
	// 如果是空树的情况下
	if (root == NULL)
	{
		// printf("NULL");
		return false;
	}

	Queue qu;
	QueueInit(&qu);

	// 如果树不是空树,先将第一个根节点入队列
	if (root != NULL)
	{
		QueuePush(&qu, root);
	}
	// 如果队列不为空,则将队列中的队头数据取出,并pop掉,如果做右子树不为空,就将左右子树也入队列

	while (!QueueEmpty(&qu))
	{
		// 取队头元素,根节点
		BTNode* front = QueueFront(&qu);
		QueuePop(&qu);

		if (front == NULL)
		{
			break;
		}

		// 这里不需要判断左右孩子为空再入队列,因为只有将空节点也入队列后面不断出队列的时候才能找到非空
		QueuePush(&qu, front->left);
		QueuePush(&qu, front->right);
	}

	while (!QueueEmpty(&qu))
	{
		// 取队头元素,根节点
		BTNode* front = QueueFront(&qu);
		QueuePop(&qu);

		if (front != NULL)
		{
			return false;
		}
	}

	QueueDestroy(&qu);

	return true;
}

测试函数:

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "BTree.h"

//void testarrtree()
//{
//	char a[] = "ABD##E#H##CF##G##";
//
//	int n = sizeof(a) / sizeof(a[0]);
//	int i = 0;
//
//	BTNode* tree_root = BinaryTreeCreate(a, n, &i);
//
//	BinaryTreePrevOrder(tree_root);
//}


// 自己手搓的树
BTNode* CreatTree()
{
	BTNode* root = BuyNode(0);
	// BTNode* root = NULL;
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	BTNode* node7 = BuyNode(7);
	BTNode* node8 = BuyNode(8);
	BTNode* node9 = BuyNode(9);
	//BTNode* node10 = BuyNode(10);

	root->left = node1;
	root->right = node2;
	node1->left = node3;
	node1->right = node4;
	node2->left = node5;
	node2->right = node6;
	node3->left = node7;
	node3->right = node8;
	node4->left = node9;
	//node8->right = node10;

	return root;
}


void testTree()
{
	// 创建一棵树
	BTNode* treeroot = CreatTree();

	// 前序遍历
	// BinaryTreePrevOrder(treeroot);

	// 中序遍历
	// BinaryTreeInOrder(treeroot);

	// 后序遍历
	// BinaryTreePostOrder(treeroot);

	// 测试树"ABD##E#H##CF##G##";
	// char a[] = "ABD##E#H##CF##G##";
	// int n = sizeof(a) / sizeof(a[0]);
	// int pi = 0;
	// BTNode* root = BinaryTreeCreate(a, n, &pi);

	// BinaryTreePrevOrder(root);

	 二叉树的销毁
	//BinaryTreeDestory(treeroot);
	//treeroot = NULL;

	 前序遍历
	// BinaryTreePrevOrder(treeroot);

	// 求节点个数
	//printf("%d", BinaryTreeSize(treeroot));

	// 求叶子节点个数
	// printf("%d\n", BinaryTreeLeafSize(treeroot));
	
	// 求树的高度
	// printf("%d\n", BinaryHeight(treeroot));

	// 求第K层节点的个数
	// printf("%d\n", BinaryTreeLevelKSize(treeroot, 5));

	//BTNode* Node = BinaryTreeFind(treeroot, 4);
	//printf("%d\n", Node->data);

	// 层序遍历
	// BinaryTreeLevelOrder(treeroot);

	// 判断是否是完全二叉树
	printf("%d\n", BinaryTreeComplete(treeroot));
}

int main()
{
	// testTree();
	// testarrtree();
	testTree();

	return 0;
}

三:二叉树的基础OJ练习:

1:965. 单值二叉树 - 力扣(LeetCode)

2:100. 相同的树 - 力扣(LeetCode)

3:101. 对称二叉树 - 力扣(LeetCode)

4:144. 二叉树的前序遍历 - 力扣(LeetCode)

5:94. 二叉树的中序遍历 - 力扣(LeetCode)

6:145. 二叉树的后序遍历 - 力扣(LeetCode)

7:572. 另一棵树的子树 - 力扣(LeetCode)

8:二叉树遍历_牛客题霸_牛客网 (nowcoder.com)

相关推荐
xiaoshiguang33 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡3 小时前
【C语言】判断回文
c语言·学习·算法
别NULL3 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇3 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
ZSYP-S4 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos4 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习4 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA5 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo5 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc5 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法