【DS】树&二叉树链式结构及实现@二叉树 —— 前中后序遍历 | 求节点个数高度等 | 层序遍历&判断是否是完全二叉树

正文开始@小边同学还爱编程吗⭐️

树&二叉树链式结构及实现

1. 树概念及结构

1.1 树的概念

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

树是递归定义的。任何树都会被分成根和子树(多个/空)

1.2 树的相关概念

💙节点的:一个节点含有的子树的个数称为该节点的度。
💙叶节点或终端节点:度为0的节点称为叶节点。
💙分支节点或非终端节点:度不为0的节点。
💛父节点或双亲节点:若一个节点含有子节点,则这个节点称为其子节点的父节点。
💛子节点或孩子节点:一个节点含有的子树的根节点称为该节点的子节点。
💛兄弟节点:具有相同父节点的节点(亲兄弟)互称为兄弟节点。如上图:B、C是兄弟节点,H、I不是
💛堂兄弟节点:双亲在同一层的节点互为堂兄弟。如上图:H、I互为堂兄弟节点
💛节点的祖先:从根到该节点所经分支上的所有节点。如上图:A、E、J都是Q的祖先
💛子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
💚树的度:一棵树中,最大的节点的度称为树的度。如上图:树的度为6
💚节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推
💚树的高度或深度:树中节点的最大层次。如上图:树的高度为4,3
💚森林:由m(m>0)棵互不相交的树的集合称为森林。并查集就是多棵树构成的森林(以后详谈)

1.3 树的表示

树结构相对线性表比较复杂,既要保存值域,也要保存结点和结点之间的关系,怎么存储呢?

实际中树有很多种表示方式如:双亲表示法,孩子表示法及孩子双亲表示法等。它们各有优缺点,我们这里重点介绍表示树结构最优方法 —— 左孩子右兄弟表示法。

方式1:假设已知树的度为N

struct TreeNode
{
    int data;
    struct TreeNode* subs[N];
}

问题:可能存在不少的空间浪费;万一没有限定树的度是多少呢?

方式2:用链表代替数组来存储其孩子节点

typedef struct TreeNode* SLDataType;
struct TreeNode
{
    int data;
    SeqList s; // SLDataType* a; //c++ vector
}

问题:结构相对复杂

方式3:结构数组存储 —— 双亲表示法(只存父亲)

struct TreeNode
{
    int parent;
    int data;
}

💛 左孩子右兄弟表示法

typedef int DataType;
struct Node
{
    //只存两个指针
    struct Node* _firstChild1; 	// 第一个孩子结点
    struct Node* _pNextBrother; // 指向其下一个兄弟结点
    DataType _data;				// 结点中的数据域
};

没有空间浪费,且可完整表示,真的很妙!

1.4 树在实际中的运用

表示文件系统的目录树结构

2. 二叉树概念及结构

树本身并不常用,下面介绍二叉树。

2.1 二叉树的概念

💛 二叉树

  1. 最大为2的树,最多只有两个孩子。

  2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树。

对于任意的二叉树都是由以下几种情况复合而成的:

现实中的二叉树 ——

2.2 特殊的二叉树

💛 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。

  1. 所有的叶子节点都在最后一层
  2. 左右的分支节点都有两个孩子

💛 完全二叉树

  1. 前(n-1)层都是满的
  2. 最后一层不一定满,但是最后一层从左到右是连续的

:满二叉树是一种特殊的完全二叉树。

1.假设树的高度为 h h h,总共有 2 h − 1 2^h-1 2h1个节点,由等比数列求和公式求得

2 0 + 2 1 + . . . 2 ( h − 1 ) = 1 ( 2 − 1 ) / ( 2 − 1 ) = 2 h − 1 2^0+2^1+ ...2^(h-1) = 1(2^-1)/(2-1) = 2^h-1 20+21+...2(h1)=1(21)/(21)=2h1

2.一棵满二叉树有 N N N个节点,高度是多少,由N = 2h-1

满二叉树高度为 h = log2(N+1)

2.3 二叉树的性质

1.一个 n n n 个结点的二叉树拥有 ( n − 1 ) (n-1) (n1) 条边
2.若规定根节点的层数为1,则一棵非空二叉树的第 i i i层上最多有 2i-1 个结点
3.度为0的度为2的永远多1个。对任何一棵二叉树,如果度为0其叶结点个数为n0 ,度为2的分支结点个数为n2 ,则有 n0= n2+1(学图论的时候啊,证过一大堆这些破玩意儿,我都忘了)

其它在堆中的性质,下一篇文章见。

关于这些性质,有一系列题目,熟悉性质就很简单

1. 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( )
A 不存在这样的二叉树
B 200
C 198
D 199

n0 = n2 + 1  n0 = 200
3.在具有 2n 个结点的完全二叉树中,叶子结点个数为( )
A n
B n+1
C n-1
D n/2

由n0 + n1 + n2 = 2n
n0 = n2 + 12n0 + n1 - 1 = 2n

完全二叉树最多度为1的节点只能是0个或者1个,
n1 = 0时,n0不是整数;n1 = 1时,n0 = n

5.一个具有767个节点的完全二叉树,其叶子节点个数为()
A 383
B 384
C 385
D 386

n0 + n1 + n2 = 768
n0 = n2 + 1
n0 = 384 选笔。
4.一棵完全二叉树的节点数位为531个,那么这棵树的高度为( )
A 11
B 10
C 8
D 12

高度为 h h h的完全二叉树的节点范围是[ 2h-1-1+1,2h-1],选笔

2.4 二叉树的存储结构

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

2.4.1 顺序存储

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

关于堆,经典的堆排序和TopK问题马上单独出文章见~

2.4.2 链式存储

二叉树的链式存储结构是指,用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,后面高阶数据结构如红黑树等会用到三叉链。

typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
     struct BinTreeNode* _pLeft; // 指向当前节点左孩子
     struct BinTreeNode* _pRight; // 指向当前节点右孩子
     BTDataType _data; // 当前节点值域
}

// 三叉链
struct BinaryTreeNode
{
     struct BinTreeNode* _pParent; // 指向当前节点的双亲
     struct BinTreeNode* _pLeft; // 指向当前节点左孩子
     struct BinTreeNode* _pRight; // 指向当前节点右孩子
     BTDataType _data; // 当前节点值域
}

今天我们要实现的是二叉树的链式结构。

3. 二叉树链式结构的实现

普通二叉树的增删查改没有什么价值,用它来存数据太复杂了。因此在这里我们不关注增删查改,关注递归遍历结构

价值体现 ——

  • 为后面学习更有用的树(在它的基础上,增加一些性质)打下基础
  • 很多oj题结构是普通二叉树。对的,马上再来更新一波题解。

老规矩,头文件附在后面了,有需要自取。

3.0 前置说明

在对二叉树的做基本操作前,需先有一棵二叉树。由于现在对二叉树结构掌握还不够深入,此处暂时手动构建一棵简单的二叉树,快速上手,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。

BTNode* BuyNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		printf("malloc failed\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->left = newnode->right = NULL;
	return newnode;
}

BTNode* CreateBinaryTree()
{
	BTNode* nodeA = BuyNode('A');
	BTNode* nodeB = BuyNode('B');
	BTNode* nodeC = BuyNode('C');
	BTNode* nodeD = BuyNode('D');
	BTNode* nodeE = BuyNode('E');
	BTNode* nodeF = BuyNode('F');

	nodeA->left = nodeB;
	nodeA->right = nodeC;
	nodeB->left = nodeD;
	nodeC->left = nodeE;
	nodeC->right = nodeF;

	return nodeA;
}

这篇文章我们就要与它为伴了😇 ——

3.1 前中后序遍历

💛 首先了解什么是所谓的前序/中序/后序的递归结构遍历。按照规则,二叉树的遍历有——

  1. 前序遍历,亦称先序遍历(Preorder Traversal) —— 访问左子树右子树。

  2. 中序遍历(Inorder Traversal) —— 访问左子树右子树。

  3. 后序遍历(Postorder Traversal) —— 访问左子树右子树

二叉树的递归遍历,是一种分治思想,即分而治之,大事化小,小事化了。(本文只写递归版本的深先,非递归版本的我们cpp见)

遍历一棵树,就可以拆分成,根,遍历左子树,遍历右子树;遍历该左子树再拆分为,左子树的根,遍历左子树的左子树,遍历左子树的右子树,以此类推。直至遍历到空节点,小事化了,再返回。前中后序递归过程类似,只不过访问/打印根节点时机不一样罢了。

上图中,访问顺序为 ——

前序	A B D NULL NULL NULL C E NULL NULL F NULL NULL
中序  NULL D NULL B NULL A NULL E NULL C NULL F NULL
后序  NULL NULL D NULL B NULL NULL E NULL NULL F C A

3.1.1 前序遍历

  • 两要素:不断向递归终止条件靠近**&**递归终止条件
  • 这里把NULL都打印,很多地方都是不打印的,但打印出来才看得到递归的本质。
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%c ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

你觉得这些递归难理解吗?三个月前我初学可能也有同样的感觉,现在看也还算是理所当然,不清晰那就去画递归展开图吧!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rl9TWmqD-1644589500703)(C:\Users\13136\AppData\Roaming\Typora\typora-user-images\image-20220210183654303.png)]

3.1.2 中序遍历

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

3.1.3 后序遍历

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

3.2 节点个数及高度等

下面都是对分而治之思想的应用,这些带返回值的递归如果觉得不清晰,就去画递归展开图理解吧。

3.2.1 二叉树的节点个数

分治:

  • 二叉树的节点个数 = 自己(1) + 左子树节点个数 + 右子树节点个数
  • 不可拆分的子问题:空节点
int BinaryTreeLeafSize(BTNode* root)
{
	return root == 0 ? 0 : 
    	 		1 + BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
    /*if (root == NULL)
		return 0;
	return 1 + BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);*/
}

求二叉树节点个数也可以采用遍历计数的方法,但要注意

// 如果不加static,递归调用建立函数栈桢,count是局部变量,不是对一个变量++
// 如果加了static,此时放在静态区,但依然有问题,求第二棵树的时候,节点数累加
// 1.遍历计数 -- 有问题
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	static int count = 0;
	count++;
	BinaryTreeSize(root->left);
	BinaryTreeSize(root->right);
	return count;
}

因此我们引入 2.输出型参数 或者 上文的返回值的分治的方法

// 2.输出型参数
void BinaryTreeSize(BTNode* root, int* pn)
{
	if (root == NULL)
		return 0;
	(*pn)++;
	BinaryTreeSize(root->left, pn);
	BinaryTreeSize(root->right, pn);
}

3.2.2 二叉树的的叶子结点个数

分治:

  • 二叉树的的叶子结点个数 = 左子树叶子节点个数 + 右子树叶子结点个数
  • 不可拆分的子问题:叶子节点,返回1
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0; //处理空树,包括子树当中为空的情况
	if (root->left == NULL && root->right == NULL)
		return 1;
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

3.2.3 二叉树第k层节点个数

分治:这个确实不容易想呢

  • 提示:求A的第四层节点个数 = 求左子树的第三层节点个数 + 求右子树的第三层节点个数
  • 不可拆分的子问题:到第1层,即叶子节点。相对距离为0,不用再往下沉了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ytuIeGBc-1644589500706)(C:\Users\13136\AppData\Roaming\Typora\typora-user-images\image-20220211152101792.png)]

int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

3.2.4 二叉树的深度

分治:

  • 二叉树的深度/高度 = 1 + 左子树、右子树的深度较大的(像后序)
  • 不可拆分的子问题:空节点
int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
		return 0;
	int leftDepth = BinaryTreeDepth(root->left);
	int rigthDepth = BinaryTreeDepth(root->right);
	return 1 + (leftDepth > rigthDepth ? leftDepth : rigthDepth);
}

注意:

	// 这样会有大量的重复计算
	return BinaryTreeDepth(root->left) > BinaryTreeDepth(root->right) ? 
        BinaryTreeDepth(root->left) + 1 : BinaryTreeDepth(root->right) + 1;

3.2.5 二叉树查找值为x的节点

分治:

  • 二叉树查找值为x的节点 = 本身是不是 + 在左子树中查找值为x的节点 + 在右子树中查找值为x的节点 (相当于前序遍历顺序查找)
  • 不可拆分的子问题:值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	BTNode* leftRet;
	BTNode* rightRet;
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;

	leftRet = BinaryTreeFind(root->left, x);
	if (leftRet)
		return leftRet;

	rightRet = BinaryTreeFind(root->right, x);
	if (rightRet)
		return rightRet;

	return NULL;
}

觉得不清晰,就在脑子里面走一遍递归过程,画递归展开图也可,画图太累了我不画了。

其实,我们把函数调用的第一层逻辑考虑清楚,整个递归问题就不大。

3.3 层序遍历

3.3.1 层序遍历

层序遍历:自上而下,自左至右逐层访问树的结点的过程。

这里要利用队列先进先出的性质。目前还是要把之前写好的文件包过来。

💛 1. 先入根

💛 2. 当前节点出来,把孩子带进去。就是这样上一层出的时候,带入下一层

💛 3. 队列为空,说明走后一层没有节点,遍历结束。

A B C D E F

❤️ 注注注意:此时队列中的数据类型树的节点。之前的Queue.h我们数据类型是int,因此现在要做一些改动。

Queue.h

//注意是树的节点--数据类型
//前置声明
struct BinaryTreeNode;
typedef struct BinaryTreeNode* QDataType;

//队列
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QueueNode;

//多个变量,那就用结构体封起来
typedef struct Queue
{
	QueueNode* head;
	QueueNode* tail;
}Queue;

层序遍历 ——

void LevelOrder(BTNode* root)
{
	if (root == NULL)
	{
		return ;
	}
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%c ", front->data);
		QueuePop(&q);

		if (front->left)
			QueuePush(&q, front->left);
		if (front->right)
			QueuePush(&q, front->right);
	}
	printf("\n");
	QueueDestroy(&q);
}

3.3.2 判断是否为完全二叉树

判断是否为完全二叉树是层序遍历的变形

遇到空了以后,出剩下的所有节点并检查

bool BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
    // 1.层序遍历小变形
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
        // 带孩子进队列,空的也要带
		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}
    // 2.遇到空后,出队检查
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

3.4 销毁

二叉树的销毁依旧是递归销毁的,相当于后序遍历

void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
		return ;
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}

为了保证接口一致,可以传一级指针,但要注意需要在外边置空,在里面置空没有作用(众所周知,形参改变不会影响实参)

	BinaryTreeDestory(root);
	root = NULL;

当然也可以传二级,那就可以在里边置空了。

BinaryTree.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

typedef char BTDataType;

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

// 二叉树前序遍历	
void PreOrder(BTNode* root);
// 二叉树中序遍历
void InOrder(BTNode* root);
// 二叉树后序遍历
void PostOrder(BTNode* root);

//求二叉树节点的个数
void BinaryTreeSize(BTNode* root,int* pn);
//int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
//二叉树的深度
int BinaryTreeDepth(BTNode* root);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

// 层序遍历
void LevelOrder(BTNode* root);
//判断一棵树是否为完全二叉树
bool BinaryTreeComplete(BTNode* root);

// 二叉树手动构建
BTNode* CreateBinaryTree();
// 二叉树销毁
void BinaryTreeDestory(BTNode* root);
//传一级二级都行:外面置空(里面置空没有用)
//这里为了保持传参的一致性传一级

BinaryTree.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"BinaryTree.h"
#include"Queue.h"

// 二叉树前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%c ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

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

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

// 1. 遍历计数的方法 
// 如果不加static,递归调用建立函数栈桢,count是局部变量,不是对一个变量++
// 如果加了static,此时放在静态区,但依然有问题,求第二棵树的时候,节点数累加
// 因此我们引入2.输出型参数或者3.返回值的方法
//int BinaryTreeSize(BTNode* root)
//{
//	if (root == NULL)
//		return 0;
//	static int count = 0;
//	count++;
//	BinaryTreeSize(root->left);
//	BinaryTreeSize(root->right);
//	return count;
//}

// 2.输出型参数
void BinaryTreeSize(BTNode* root, int* pn)
{
	if (root == NULL)
		return 0;
	(*pn)++;
	BinaryTreeSize(root->left, pn);
	BinaryTreeSize(root->right, pn);
}

 3. 分治
//int BinaryTreeSize(BTNode* root)
//{
//	return root == 0 ? 0 : 1 + BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
//	/*if (root == NULL)
//		return 0;
//	return 1 + BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);*/
//}

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0; //不仅仅是空树的情况,走到某个子树为空
	if (root->left == NULL && root->right == NULL)
		return 1;
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

//二叉树的深度
int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
		return 0;
	int leftDepth = BinaryTreeDepth(root->left);
	int rigthDepth = BinaryTreeDepth(root->right);
	return 1 + (leftDepth > rigthDepth ? leftDepth : rigthDepth);
}

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

	leftRet = BinaryTreeFind(root->left, x);
	if (leftRet)
		return leftRet;

	rightRet = BinaryTreeFind(root->right, x);
	if (rightRet)
		return rightRet;

	return NULL;
}


void LevelOrder(BTNode* root)
{
	if (root == NULL)
	{
		return ;
	}
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%c ", front->data);
		QueuePop(&q);

		if (front->left)
			QueuePush(&q, front->left);
		if (front->right)
			QueuePush(&q, front->right);
	}
	printf("\n");
	QueueDestroy(&q);
}

bool BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

BTNode* BuyNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		printf("malloc failed\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->left = newnode->right = NULL;
	return newnode;
	printf("\n");
}


BTNode* CreateBinaryTree()
{
	BTNode* nodeA = BuyNode('A');
	BTNode* nodeB = BuyNode('B');
	BTNode* nodeC = BuyNode('C');
	BTNode* nodeD = BuyNode('D');
	BTNode* nodeE = BuyNode('E');
	BTNode* nodeF = BuyNode('F');
	BTNode* nodeG = BuyNode('G');

	nodeA->left = nodeB;
	nodeA->right = nodeC;
	nodeB->left = nodeD;
	nodeC->left = nodeE;
	nodeC->right = nodeF;
	nodeB->right = nodeG;

	return nodeA;
}

void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
		return ;
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
	root = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"BinaryTree.h"
#include"Queue.h"

int main()
{
	BTNode* root = CreateBinaryTree();
	/*PreOrder(root);
	printf("\n");

	InOrder(root);
	printf("\n");

	PostOrder(root);
	printf("\n");*/

	// 求二叉树节点的个数
	1.遍历计数又问题哈
	/*printf("size:%d\n", BinaryTreeSize(root));
	printf("size:%d\n", BinaryTreeSize(root));*/

	//2.求二叉树节点的个数
	int n1 = 0;
	BinaryTreeSize(root, &n1);
	printf("BinaryTreeSize:%d\n", n1);

	//3. 分治
	//printf("size:%d\n", BinaryTreeSize(root));
	
	printf("LeafSize:%d\n", BinaryTreeLeafSize(root));

	printf("BinaryTreeLevelKSize:%d\n", BinaryTreeLevelKSize(root,4));

	printf("BinaryTreeDepth:%d\n", BinaryTreeDepth(root));

	BTNode* p = BinaryTreeFind(root, 'D');
	printf("BinaryTreeFind:%c\n", p->data);

	LevelOrder(root);

	printf("BinaryTreeComplete:%d\n", BinaryTreeComplete(root));

	BinaryTreeDestory(root);
	root = NULL;
	return 0;
}

Queue.h

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

//注意是树的节点--数据类型
//前置声明
struct BinaryTreeNode;
typedef struct BinaryTreeNode* QDataType;

//队列
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QueueNode;

//多个变量,那就用结构体封起来
typedef struct Queue
{
	QueueNode* head;
	QueueNode* tail;
}Queue;

//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);

//入队
void QueuePush(Queue* pq, QDataType x);
//出队
void QueuePop(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);
//求队列中有效元素个数
int QueueSize(Queue* pq);
//判断队列是否为空?
bool QueueEmpty(Queue* pq);

Queue.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"Queue.h"

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QueueNode* cur = pq->head;
	while (cur)
	{
		QueueNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	newnode->data = x;
	newnode->next = NULL;
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;//迭代
	}
}

void QueuePop(Queue* pq)
{
	assert(pq);
 	assert(!QueueEmpty(pq));
	QueueNode* newhead = pq->head->next;
	free(pq->head);
	pq->head = newhead;
	if (pq->head == NULL)
	{
		pq->tail = NULL;
	}
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

int QueueSize(Queue* pq)
{
	assert(pq);
	int size = 0;
	QueueNode* cur = pq->head;
	while (cur)
	{
		cur = cur->next;
		size++;
	} 
	return size;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

马上更新一波二叉树经典题解@边同学

  • 27
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浮光 掠影

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值