【ONE·Data || 链式二叉树】

总言

  简单实现链式二叉树(C语言)


  
  

整体模式

BinaryTree.c

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include"Queue.h"




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


//二叉树中的单个节点的创建
BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	assert(node);
	node->data = x;
	node->left = node->right = NULL;
	return node;
}




//
//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)//若为空则返回
	{
		printf("# ");
		return;
	}
	printf("%d ", root->data);//先访问根
	PreOrder(root->left);//再访问左子树
	PreOrder(root->right);//最后访问右子树
	//递归问题
}

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	InOrder(root->left);//先访问左子树
	printf("%d ", root->data);//再访问根
	InOrder(root->right);//最后访问右子树
}

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	PostOrder(root->left);//先访问左子树
	PostOrder(root->right);//后访问右子树
	printf("%d ", root->data);//最后访问根
}
//







//
//二叉树的层序遍历:需要使用队列辅助完成,队列中存储的是指向二叉树结点的指针(思考:为什么存储的数据类型是这个?)
void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)//当树当前节点不为空时
	{
		QueuePush(&q, root);//将指向树的节点的指针存入队列中
	}
	while (!QueueEmpty(&q))//当队列不为空时
	{
		BTNode* front = QueueFront(&q);//出队头数据(出根节点)
		printf("%d ", front->data);//打印此时二叉树的节点
		QueuePop(&q);//删除队头数据
		//此处需要注意QueuePop的含义,删除的是队列中队头节点,而非实际二叉树中的节点
		//这意味着我们只是借助队列的特性来辅佐达成二叉树的层序遍历,对于队列底层如何实现并不关心(低耦合高内聚)。

		if (front->left)//队列中进当前节点下一层的数据
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	QueueDestroy(&q);
}
//






//
//判断二叉树是否为完全二叉树
//思路:完全二叉树在首次遇到NULL后,其后所有节点都为NULL
int TreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);

	if (root)//当树当前节点不为空时
	{
		QueuePush(&q, root);//将指向树的节点的指针存入队列中
		//(也可以把二叉树单个节点,即结构体拷贝存储在队列中,只是结构体本身占据内存空间较大,因此选择结构体对应的指针较佳)
	}

	while (!QueueEmpty(&q))//当队列不为空时
	{
		BTNode* front = QueueFront(&q);//出队头数据(出根节点)
		QueuePop(&q);//删除队头数据

		if (front)//若当前节点不为NULL,则继续将节点指针存入队列
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
		else
		{
			break;//若当前节点为NULL,则跳出层序遍历,只需要判断从该节点后的所有入队指针是否都为空即可。
		}
	}

	//1、当在队列中首次遇到NULL时,若后续所有指针全为NULL,则为完全二叉树
	//2、若后续指针中有非空的情况,则非完全二叉树
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front )
		{
			QueueDestroy(&q);
			return false;
		}
	}

	QueueDestroy(&q);
	return true;
}
//





//
//求二叉树中节点的总数
//思路:在二叉树的遍历基础上,定义一个全局变量用于计数(此处用于计数的变量必须是全局变量)
int count = 0;
void BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	//此处遍历可在前序、中序、后序中任选其一。
	count++;//此句话相当于遍历中的当前节点的打印(只不过此处把打印换作计数)
	BinaryTreeSize(root->left);//此句话相当于遍历左子树
	BinaryTreeSize(root->right);//此句话相当于遍历右子树
}


//求二叉树中节点的总数·改进思路
//思路:分治算法
int BinaryTreeSize2(BTNode* root)
{
	return root == 0 ? 0 :
		(BinaryTreeSize2(root->left) + BinaryTreeSize2(root->right)+1);
	//节点统计:左子树总节点+右子树总节点+根节点
	//根据分而治之的思想,以每个节点及其左右孩子为最小单元,最终函数会递归到叶子节点,即两孩子root->left和root->right为NULL时,此时+1统计的是自身。
	//对于有孩子节点时,函数会继续递归下去直至上述情况统计到自身,而当所有值层层返回时统计到的就是总数。

	//关于上述的一个举例:
	//对于层层递归:假若学校要统计学生总数,只需要把任务分配给各个院系,各院系又会将任务分配给各年级辅导员,辅导员将任务分配给各班级班长,班长再通知到宿舍长。
	//对于递归后层层返回:班长通过宿舍长的汇报得到一个班总体人数,辅导员根据各班班长的汇报得到其年级总人数,各年级辅导员所统计人数汇总即得一个院系人数,各院系人数汇总即得全校学生人数。
}
//








/
//求二叉树中叶子节点的总数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	return (root->left == NULL && root->right == NULL) ? 1 :
		(BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right));
}


//若用全局变量:
void BinaryTreeLeafSize1(BTNode* root)
{
	if (root == NULL)
		return;
	if (root->left == NULL && root->right == NULL)
		count++;
	BinaryTreeLeafSize1(root->left);
	BinaryTreeLeafSize1(root->right);
}
//





//
//求二叉树中第K层的节点总数
//思路:转化为子问题
//要求二叉树中第K层的节点总数,即求左子树的第K-1层节点总数+右子树中第K-1层节点总数
int TreeKLevelSize(BTNode* root,int k)//此处认为K≥1,即从第一层开始计数
{
	assert(k >= 1);

	if (root == NULL)
	{
		return 0;
	}

	if (k == 1)//K不会小于0,因为此处当K==1时触发if语句返回,assert处断言是为了确保形参中的K不是任意数
	{
		return 1;
	}
	
	return TreeKLevelSize(root->left,k-1) + TreeKLevelSize(root->right,k-1);//递归中层数递减的写法

}
//




//
//求二叉树的深度
//思路:仍旧是分而治之的子问题,只是上述是求节点总数,即相加,这里是求深度最深的分支,即比较。
int  BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
		return 0;
	int ret1 = BinaryTreeDepth(root->left);
	int ret2 = BinaryTreeDepth(root->right);
	return ret1 > ret2 ? ret1+1 : ret2+1;
}
//




//
//二叉树查找值为X的节点(补充说明:若有多个符合条件的值,只需要返回首次找到的即可)
//思路:遍历查找二叉树,
BTNode* TreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)//若为根,则返回空
	{
		return NULL;
	}

	if (root->data == x)//对当前节点,若找到,则返回对应节点
	{
		return root;
	}

	BTNode* ret1 = TreeFind(root->left, x);//若当前节点没找到,则找当前节点的左子树
	if (ret1)//此处添加if判断语句,则单独对左子树的值进行判断,这样若左子树中有目标值时,就不用遍历右子树寻找。
		return ret1;
	BTNode* ret2 = TreeFind(root->right, x);//若当前节点的左子树没找到,则找当前节点的找右子树
	if (ret2)
		return ret2;
	//虽然也可以使用中序或后序,但倘若当前的根节点为目标节点时,使用中序、后序都会额外先遍历子树再访问根,即多做了几步操作
	//而使用前序则会先访问根节点后访问其子树,从效率上相对更优。

	return NULL;//都找不到时
}
//






//
//二叉树的销毁
//释义:后续遍历销毁二叉树节点比较方便
void DestroyTree(BTNode* root)
{
	if (root == NULL)
		return;

	//销毁左子树
	DestroyTree(root->left);
	//销毁右子树
	DestroyTree(root->right);
	//销毁根节点
	free(root);

}
//







//手搓的二叉树
BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);

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

	return node1;
}




int main()
{
	//手搓二叉树
	BTNode* root = CreatBinaryTree();

	//二叉树的遍历
	printf("Preorder Traversal:");
	PreOrder(root);
	printf("\n");

	printf("Inorder Traversal:");
	InOrder(root);
	printf("\n");

	printf("postorder Traversal:");
	PostOrder(root);
	printf("\n");



	//统计二叉树节点总数
	count = 0;//使用全局变量需要注意的细节,此处为防止多次调用节点统计函数时count累加,故需要在每次调用该函数前对count归零。
	BinaryTreeSize(root);
	printf("BinaryTreeSize:%d\n", count);

	count = 0;
	BinaryTreeSize(root);
	printf("BinaryTreeSize:%d\n", count);
	//上述的统计方法仍然存在问题,在多线程并用时会学习此部分知识



	//统计二叉树节点总数·思路改进版
	printf("BinaryTreeSize:%d\n", BinaryTreeSize2(root));



	//统计二叉树叶子节点的总数
	printf("Binarytree Size:%d\n", BinaryTreeLeafSize(root));

	count = 0;
	BinaryTreeLeafSize1(root);
	printf("Binarytree Size:%d\n", count);



	//求二叉树中第K层的节点总数
	printf("KLevelSize K=2:%d\n", TreeKLevelSize(root, 2));
	printf("KLevelSize K=3:%d\n", TreeKLevelSize(root, 3));
	printf("KLevelSize K=4:%d\n", TreeKLevelSize(root, 4));



	//求二叉树的节点深度
	printf("BinarytreeDepth:%d\n", BinaryTreeDepth(root));



	//二叉树的层序遍历
	printf("LevelOrder Traversal:");
	LevelOrder(root);

	//判断二叉树是否为完全二叉树
	printf("BinaryTreeComplete:%d \n", TreeComplete(root));


	//二叉树的销毁
	DestroyTree(root);

	return 0;
}

  
  

Queue.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS

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


struct BinaryTreeNode;//前置声明,解决头文件包含问题

//定义一个单链表用作实现队列
typedef struct BinaryTreeNode* QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue
{
	//QueueSize的一种实现方法,在此间新增一个用于统计结点的变量,随着对队列变动而变动
	//int size;
	QNode* head;
	QNode* 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);//提取队尾数据
bool QueueEmpty(Queue* pq);//判断队列是否为空
int QueueSize(Queue* pq);//查看队列总数

  
  

Queue.c

#include"Queue.h"


void QueueInit(Queue* pq)
{
	assert(pq);//初始化队列结点,结点自然不能为空指针
	pq->head = pq->tail = NULL;
}


//队列插入,尾插
void QueuePush(Queue* pq, QDataType x)
{
	//分为两种情况,其一为链表(队列)为空时,其二为链表(队列)中有结点时
	//当链表为空时,对tail、head都要进行处理;当链表中有结点时,head已有确切指向,只需要对tail进行处理(每链接一个结点,tail指向需要跟随变动)
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));//开辟新结点,注意此处单链表与顺序表的不同之处
	if (newnode == NULL)//判空
	{
		perror("QueuePush::malloc");
		exit(-1);
	}
	//处理结点中数据:
	newnode->data = x;
	newnode->next = NULL;
	//处理指向结点的指针关系:
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;//已存在结点,只需把结点关系链接上即可,并更新尾指针指向
		pq->tail = newnode;//pq->tail->next
	}
}



//队列删除,头删(此处可看出为什么选择单链表较优,头插在顺序表中要挪动数据,在链表只需要更改头指针和释放对应结点)
void QueuePop(Queue* pq)
{
	//思考问题:头删界限,由于结点有限个,头删有下限
	//此外,为方便寻找头删后的链表头结点,使用保存下一个结点的方式进行头删,循环下去最终头指针将指向NULL,
	//而此时尾指针tail指向NULL前的尾结点,该结点已经被free,则存在tail指向为野指针的问题,需要注意考虑
	assert(pq);//用于检查函数传参进入的指针是否为空(即它可能不指向链表,而指向NULL)
	assert(!QueueEmpty(pq));//用于检查链表本身是否为空(即pq指针指向链表,但这是一个空链表)

	if (pq->head->next == NULL)//链表中只剩一个结点的情况,即链表头指针和尾指针此处重合,该结点next指向NULL
	{
		free(pq->head);
		pq->head = pq->tail = NULL;//此时为了防止tail为野指针,需要将二者置空
	}
	else//当链表中存在多个结点时(此处如果使用带哨兵位的头结点时,头删到下限即只剩下哨兵位,则细节有些区别)
	{
		QNode* next = pq->head->next;//保存头结点后一个结点
		free(pq->head);//头删
		pq->head = next;//新的头结点
	}
}


//检查链表(队列)是否为空的函数
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == 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 QueuSize(Queue* pq)
{
	//若不在初始结构体中定义变量用于统计结点数目,则在此处需要遍历链表主动求得(效率相对较低)
	assert(pq);
	QNode* cur = pq->head;
	int size=0;
	while (cur)
	{
		++size;
		cur = cur->next;
	}
	return size;
}


//队列销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur=pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

  
  

分块模式

手搓二叉树的检验

在这里插入图片描述

//手搓的二叉树
BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);

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

	return node1;
}

int main()
{
	//手搓二叉树
	BTNode* root = CreatBinaryTree();
	return 0;
}

  
  

三种遍历方式及其写法

前序、中序、后续遍历简单介绍:

在这里插入图片描述
  代码如下:

//
//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)//若为空则返回
	{
		printf("# ");
		return;
	}
	printf("%d ", root->data);//先访问根
	PreOrder(root->left);//再访问左子树
	PreOrder(root->right);//最后访问右子树
	//递归问题
}

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	InOrder(root->left);//先访问左子树
	printf("%d ", root->data);//再访问根
	InOrder(root->right);//最后访问右子树
}

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	PostOrder(root->left);//先访问左子树
	PostOrder(root->right);//后访问右子树
	printf("%d ", root->data);//最后访问根
}
//

int main()
{
	//手搓二叉树
	BTNode* root = CreatBinaryTree();

	//二叉树的遍历
	printf("Preorder Traversal:");
	PreOrder(root);
	printf("\n");

	printf("Inorder Traversal:");
	InOrder(root);
	printf("\n");

	printf("postorder Traversal:");
	PostOrder(root);
	printf("\n");
	return 0}

  结果示意图:
在这里插入图片描述
  
  

前序、中序、后序遍历递归部分示意图:

  前序:

//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)//若为空则返回
	{
		printf("# ");
		return;
	}
	printf("%d ", root->data);//先访问根
	PreOrder(root->left);//再访问左子树
	PreOrder(root->right);//最后访问右子树
	//递归问题
}

在这里插入图片描述
  中序:

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	InOrder(root->left);//先访问左子树
	printf("%d ", root->data);//再访问根
	InOrder(root->right);//最后访问右子树
}

在这里插入图片描述
  后序:

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	PostOrder(root->left);//先访问左子树
	PostOrder(root->right);//后访问右子树
	printf("%d ", root->data);//最后访问根
}

在这里插入图片描述
  
  

求二叉树结点总数

思路:

//求二叉树中节点的总数
//思路:在二叉树的遍历基础上,定义一个全局变量用于计数(此处用于计数的变量必须是全局变量)
int count = 0;
void BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	//此处遍历可在前序、中序、后序中任选其一。
	count++;//此句话相当于遍历中的当前节点的打印(只不过此处把打印换作计数)
	BinaryTreeSize(root->left);//此句话相当于遍历左子树
	BinaryTreeSize(root->right);//此句话相当于遍历右子树
}

在这里插入图片描述

思路改进:

//求二叉树中节点的总数·改进思路
//思路:分治算法
int BinaryTreeSize2(BTNode* root)
{
	return root == 0 ? 0 :
		(BinaryTreeSize2(root->left) + BinaryTreeSize2(root->right)+1);
	//节点统计:左子树总节点+右子树总节点+根节点
	//根据分而治之的思想,以每个节点及其左右孩子为最小单元,最终函数会递归到叶子节点,即两孩子root->left和root->right为NULL时,此时+1统计的是自身。
	//对于有孩子节点时,函数会继续递归下去直至上述情况统计到自身,而当所有值层层返回时统计到的就是总数。

	//关于上述的一个举例:
	//对于层层递归:假若学校要统计学生总数,只需要把任务分配给各个院系,各院系又会将任务分配给各年级辅导员,辅导员将任务分配给各班级班长,班长再通知到宿舍长。
	//对于递归后层层返回:班长通过宿舍长的汇报得到一个班总体人数,辅导员根据各班班长的汇报得到其年级总人数,各年级辅导员所统计人数汇总即得一个院系人数,各院系人数汇总即得全校学生人数。
}

在这里插入图片描述
  
  

二叉树中叶子节点的总数

/
//求二叉树中叶子节点的总数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	return (root->left == NULL && root->right == NULL) ? 1 :
		(BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right));
}


//若用全局变量:
void BinaryTreeLeafSize1(BTNode* root)
{
	if (root == NULL)
		return;
	if (root->left == NULL && root->right == NULL)
		count++;
	BinaryTreeLeafSize1(root->left);
	BinaryTreeLeafSize1(root->right);
}
//

在这里插入图片描述
  
  

二叉树中第K层的节点总数

//
//求二叉树中第K层的节点总数
//思路:转化为子问题
//要求二叉树中第K层的节点总数,即求左子树的第K-1层节点总数+右子树中第K-1层节点总数
int TreeKLevelSize(BTNode* root,int k)//此处认为K≥1,即从第一层开始计数
{
	assert(k >= 1);

	if (root == NULL)
	{
		return 0;
	}

	if (k == 1)//K不会小于0,因为此处当K==1时触发if语句返回,assert处断言是为了确保形参中的K不是任意数
	{
		return 1;
	}
	
	return TreeKLevelSize(root->left,k-1) + TreeKLevelSize(root->right,k-1);//递归中层数递减的写法

}
//

在这里插入图片描述
  
  

二叉树深度遍历示意图

//
//求二叉树的深度
//思路:仍旧是分而治之的子问题,只是上述是求节点总数,即相加,这里是求深度最深的分支,即比较。
int  BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
		return 0;
	int ret1 = BinaryTreeDepth(root->left);
	int ret2 = BinaryTreeDepth(root->right);
	return ret1 > ret2 ? ret1+1 : ret2+1;
}
//

在这里插入图片描述
  
  

二叉树查找值为X的节点

//
//二叉树查找值为X的节点(补充说明:若有多个符合条件的值,只需要返回首次找到的即可)
//思路:遍历查找二叉树,
BTNode* TreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)//若为根,则返回空
	{
		return NULL;
	}

	if (root->data == x)//对当前节点,若找到,则返回对应节点
	{
		return root;
	}

	BTNode* ret1 = TreeFind(root->left, x);//若当前节点没找到,则找当前节点的左子树
	if (ret1)//此处添加if判断语句,则单独对左子树的值进行判断,这样若左子树中有目标值时,就不用遍历右子树寻找。
		return ret1;
	BTNode* ret2 = TreeFind(root->right, x);//若当前节点的左子树没找到,则找当前节点的找右子树
	if (ret2)
		return ret2;
	//虽然也可以使用中序或后序,但倘若当前的根节点为目标节点时,使用中序、后序都会额外先遍历子树再访问根,即多做了几步操作
	//而使用前序则会先访问根节点后访问其子树,从效率上相对更优。

	return NULL;//都找不到时
}
//

  
  

层序遍历

在这里插入图片描述

//
//二叉树的层序遍历:需要使用队列辅助完成,队列中存储的是指向二叉树结点的指针(思考:为什么存储的数据类型是这个?)
void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)//当树当前节点不为空时
	{
		QueuePush(&q, root);//将指向树的节点的指针存入队列中
	}
	while (!QueueEmpty(&q))//当队列不为空时
	{
		BTNode* front = QueueFront(&q);//出队头数据(出根节点)
		printf("%d ", front->data);//打印此时二叉树的节点
		QueuePop(&q);//删除队头数据
		//此处需要注意QueuePop的含义,删除的是队列中队头节点,而非实际二叉树中的节点
		//这意味着我们只是借助队列的特性来辅佐达成二叉树的层序遍历,对于队列底层如何实现并不关心(低耦合高内聚)。

		if (front->left)//队列中进当前节点下一层的数据
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	QueueDestroy(&q);
}
//

  
  

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

在这里插入图片描述

//
//判断二叉树是否为完全二叉树
//思路:完全二叉树在首次遇到NULL后,其后所有节点都为NULL
int TreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);

	if (root)//当树当前节点不为空时
	{
		QueuePush(&q, root);//将指向树的节点的指针存入队列中
		//(也可以把二叉树单个节点,即结构体拷贝存储在队列中,只是结构体本身占据内存空间较大,因此选择结构体对应的指针较佳)
	}

	while (!QueueEmpty(&q))//当队列不为空时
	{
		BTNode* front = QueueFront(&q);//出队头数据(出根节点)
		QueuePop(&q);//删除队头数据

		if (front)//若当前节点不为NULL,则继续将节点指针存入队列
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
		else
		{
			break;//若当前节点为NULL,则跳出层序遍历,只需要判断从该节点后的所有入队指针是否都为空即可。
		}
	}

	//1、当在队列中首次遇到NULL时,若后续所有指针全为NULL,则为完全二叉树
	//2、若后续指针中有非空的情况,则非完全二叉树
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front )
		{
			QueueDestroy(&q);
			return false;
		}
	}

	QueueDestroy(&q);
	return true;
}
//

  
  

二叉树的销毁

//
//二叉树的销毁
//释义:后续遍历销毁二叉树节点比较方便
void DestroyTree(BTNode* root)
{
	if (root == NULL)
		return;

	//销毁左子树
	DestroyTree(root->left);
	//销毁右子树
	DestroyTree(root->right);
	//销毁根节点
	free(root);

}
//

  
  

  
  

  
  

  
  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值