二叉树的实现

发布于:2024-05-30 ⋅ 阅读:(81) ⋅ 点赞:(0)

一、结构体声明和函数声明

#pragma once

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>      
#include<assert.h>
#include<string.h>
#include<stdbool.h>

//二叉树

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

// 通过前序遍历的数组"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);
// 二叉树第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);





//队列

typedef BTNode* QDataType;  //将队列的元素类型设置为二叉树指针类型
// 链式结构:表示队列 
typedef struct QListNode
{
    struct QListNode* next;
    QDataType data;
}QNode;

// 队列的结构 
typedef struct Queue
{
    QNode* front; //指向队列的第一个结点
    QNode* rear;//指向队列的最后一个结点
    int size;//记录队列中一共有几个元素
}Queue;



// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType QueueFront(Queue* q);
// 获取队列队尾元素 
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

二、二叉树各个函数的实现

1.二叉树构建函数: BinaryTreeCreate

利用前序遍历数组构建二叉树

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)//n为字符串长度,要使用下标i的指针,
     //这样才可以改变其的值
{
	if (*pi == n - 1)//如果字符数组的下标到达字符串长度-1 的位置,说明已经构建完成
		return NULL;

		 //如果是空节点,就不要申请空间
		if (a[*pi] == '#')
		{
			(*pi)++;
			return NULL;
		}

			//如果不是空节点,要申请一个空间存储结点的数据
			BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
			if (newnode == NULL)
			{
				perror("malloc fail");
				return NULL;
			}
			//存储当前的字符串中的数据信息
			newnode->data = a[*pi];
			(*pi)++;

		//构建左右子树
		newnode->left = BinaryTreeCreate(a,n,pi);
		newnode->right = BinaryTreeCreate(a, n, pi);

		return newnode;
}

 2.二叉树销毁函数: BinaryTreeDestory

确保能够将所有结点都删除,所有要采用后序遍历的方法来进行对每一个结点的删除

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

	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);//释放结点
}



3.求二叉树节点个数函数: BinaryTreeSize

结点为空,说明没有结点,否则其等于:左子树结点+右子树结点+1(自己本身)

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)//结点为空,返回0,否则返回,左子树+右子树+1
{
	if (root == NULL)  //如果该结点为空,返回0
		return 0;

	//每个结点,都是由左子树的结点个数+右子树的结点个数+1(自己本身)
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;//左子树+右子树+1
}

 

 4.求二叉树叶子节点个数函数: BinaryTreeLeafSize

如果左右子树都为空,说明是叶子结点,如果不是,继续判断它的左右孩子结点的情况

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)//如果该结点为空,返回0
		return 0;

	if (root->left == NULL && root->right == NULL)//如果左右子树都为空,说明是叶子结点,返回1
		return 1;

	//不是叶子结点,遍历其左,右子树
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

 

5. 求二叉树第k层节点个数函数:BinaryTreeLevelKSize

 空结点返回0,第一层返回1,其他层 返回 左子树的k-1层+右子树的k-1层的 结点总和

// 二叉树第k层节点个数
//空结点返回0,第一层返回1,其他层 返回 左子树的k-1层+右子树的k-1层的 结点总和
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)//如果该结点为空,返回0
		return 0;

	if (k == 1)  //第一层只有一个结点,返回1
		return 1;
	
	//其他层 返回 左子树的k-1层+右子树的k-1层的 结点总和
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

 


 6.二叉树查找值为x的节点函数:BinaryTreeFind

每次查找都要创建一个二叉树节点类型的临时变量,来记录每次的查找结果,并将其返回

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)//如果结点为空,返回NULL,说明没找到
		return NULL;

	if (root->data == x)  //如果该结点的值等于要寻找的值,就将该结点返回
		return root;

	BTNode* ret1 = BinaryTreeFind(root->left, x);   //保证每次都先找该结点的左孩子,后找右孩子
	if (ret1)   //如果找到的不是NULL,就将其结点返回
		return ret1;

	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)     //如果找到的不是NULL,就将其结点返回
		return ret2;

	return NULL;  //没找到返回NULL
}

 

 7.二叉树前序遍历函数:  BinaryTreePrevOrder

先访问根结点,再访问左子树和右子树

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

	printf("%c", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

 

 8.二叉树中序遍历函数:BinaryTreeInOrder

先访问左子树,再是根节点,最后是右子树

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}

	BinaryTreeInOrder(root->left);
	printf("%c", root->data);
	BinaryTreeInOrder(root->right);
}


 9.二叉树后序遍历函数:BinaryTreePostOrder

先访问左子树和右子树,最后访问根结点

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

 


 10.层序遍历函数:BinaryTreeLevelOrder

(1)思路方法

使用队列的性质,先进先出,出元素在队头,入元素在队尾。

首先将树的根节点入队列,然后将队头元素出队列,这时还要将这个刚出队列的队头结点的左右孩子结点入队列,(如果非空入队列,空结点不要入队列)。

使用循环,直至队列为空,循环停止。按层次依次输出所有值

因为队头元素每出队一次,都在更新,所以所有的结点都是按照层次顺序依次进入队列,然后依次出队列

(2)队列的实现代码:
// 初始化队列 
void QueueInit(Queue* q)
{
    assert(q);
    q->front = NULL;  //初始化为NULL
    q->rear = NULL;//初始化为NULL
    q->size = 0;  //初始化个数为0
}

// 队尾入队列 
void QueuePush(Queue* q, QDataType data)
{
    assert(q);

    QNode* newnode = (QNode*)malloc(sizeof(QNode));//申请新的结点
    if (newnode == NULL)
    {
        perror("malloc fail");
        return;
    }
    else
    {
        newnode->data = data;
        newnode->next = NULL;

        if (q->front == NULL)  //如果front指针指向的是NULL,说明插入前队列是空队列
        {
            q->front = newnode;
            q->rear = newnode;
        }
        else        //front指向的不是NULL,说明不是空队列
        {
            q->rear->next = newnode;
            q->rear = newnode;
        }
        q->size++;  //插入完,个数加1
    }
}

// 队头出队列 
void QueuePop(Queue* q)//出队就是头删
{
    assert(q);
    assert(q->size != 0);//队列不为空

    QNode* head = q->front;  //找到头结点
    if (head->next == NULL)//如果出队之前,前队列只有一个结点  
    {
        free(head);   //释放头结点,后front 和rear都要指向NULL,表示现在是空队列
        head = NULL;
        q->front = q->rear = NULL;
        q->size = 0;     //个数置为0
        return;
    }
    else   //出队前,队列有两个及其以上的结点数
    {
        QNode* del = head;
        head = head->next; //更新头结点
        free(del);
        del = NULL;
        q->front = head;   //将front 指向更新后的头结点
        q->size--;//个数减1
    }
}

// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{
    assert(q);

    return q->front->data;
}

// 获取队列队尾元素 
QDataType QueueBack(Queue* q)
{
    assert(q);

    return q->rear->data;
}

// 获取队列中有效元素个数 
int QueueSize(Queue* q)
{
    assert(q);

    return q->size;
}

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
    assert(q);

    if (q->front == NULL)//队列为空,返回1
        return 1;
    else
        return 0;
}

// 销毁队列 
void QueueDestroy(Queue* q)
{
    assert(q);

    QNode* del = q->front;  //如果是空队列,就直接返回NULL,不要释放结点
    if (del == NULL)
    {
        ;
    }
    else
    {
        QNode* cur = del->next;
        while (del != NULL)    //逐个释放结点
        {

            free(del);
            del = cur;
            if (cur != NULL)
                cur = cur->next;
        }
    }
    //队头指针和队尾指针都是要置NULL的,size都是要置为0
    q->front = q->rear = NULL;
    q->size = 0;
}

 

(3)层次遍历算法代码
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)//利用队列实现
{
	Queue Q;
	QueueInit(&Q);//初始化队列

	if(root!=NULL)
	QueuePush(&Q, root);//根结点入队列

	while (!QueueEmpty(&Q))
	{
		BTNode* temp = QueueFront(&Q);//取队头元素  
		printf("%c", temp->data);//打印队头结点 所对应的数据元素
		QueuePop(&Q);//队头元素出队

		//队头元素出队后,将其左右两个孩子结点入队 (孩子结点为空,没有进入队列)
		if(temp->left!=NULL)    //左孩子不为空,入队列
		QueuePush(&Q, temp->left);

		if (temp->right != NULL) //右孩子不为空,入队列
		QueuePush(&Q, temp->right);
	}
	QueueDestroy(&Q);//最后销毁队列
}

 

 


11. 判断二叉树是否是完全二叉树函数: BinaryTreeComplete

(1)思路方法

使用队列的性质,先进先出,出元素在队头,入元素在队尾。

首先将树的根节点入队列,然后将队头元素出队列,这时还要将这个刚出队列的队头结点的左右孩子结点入队列,(空与非空结点都入队列)。

先使用一个循环,队列为非空为循环条件,元素依次人队和出队

如果队头元素出队时,遇到空结点,就跳出循环。说明找到了一个空节点
现在要再次使用循环,判断在空节点之后是否有出现非空结点,有说明不是完全二叉树,没有说明是完全二叉树

(2)算法代码
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue Q;
	QueueInit(&Q);//初始化队列

	if (root != NULL)
		QueuePush(&Q, root);//根结点入队列

	while (!QueueEmpty(&Q))
	{
		BTNode* temp = QueueFront(&Q);//取队头结点元素
		QueuePop(&Q);//队头元素出队
		if (temp == NULL)  //如果出队的队头元素为NULL,就跳出循环,
			                    //寻找接下来有没有非空结点
		{
			break;
		}

		//队头元素出队后,将其左右两个孩子结点入队 (孩子结点为空,也进入了队列)
			QueuePush(&Q, temp->left);//左孩子入队列
			QueuePush(&Q, temp->right); //右孩子入队列
	}

	//再次使用循环,判断在空节点之后是否有出现非空结点,有说明不是完全二叉树,没有说明是完全二叉树
	while (!QueueEmpty(&Q))
	{
		QDataType temp = QueueFront(&Q);//取队头元素  (QDataType是QNode*的类型)
		QueuePop(&Q);//队头元素出队

		if (temp != NULL)  //如果找到队头元素不是空,说明不是完全二叉树,返回false
		{
			QueueDestroy(&Q);//最后销毁队列
			return false;
		}
	}

	QueueDestroy(&Q);//最后销毁队列
	return true;    //是完全二叉树,返回true
}

 

 

三、测试 

int main()
{
	char arr[] = "ABD##E#H##CF##G##";
	int len = strlen(arr);
	int i = 0;
	//构建二叉树
	BTNode* root = BinaryTreeCreate(arr, len, &i);

	//水平遍历
	BinaryTreeLevelOrder(root);
	printf("\n");

	int cur = BinaryTreeComplete(root);
	if (cur == 1)
		printf("是完全二叉树\n");
	else
		printf("不是完全二叉树\n");


	//前序遍历
	printf("前序遍历结点:");
	BinaryTreePrevOrder(root);
	printf("\n");
	//中序遍历
	printf("中序遍历结点:");
	BinaryTreeInOrder(root);
	printf("\n");
	//后序遍历
	printf("后序遍历结点:");
	BinaryTreePostOrder(root);
	printf("\n");


	int a=BinaryTreeLevelKSize(root, 4);
	printf("该层有%d个\n", a);

	int b=BinaryTreeLeafSize(root);
	printf("叶子结点有%d个\n", b);

	 int c=	BinaryTreeSize(root);
	 printf("总结点有%d个\n", c);

	int d=BinaryTreeDepth(root);
	printf("树深为%d\n", d);

	BinaryTreeDestory(root);//销毁二叉树
	return 0;
}

 

结果:


网站公告

今日签到

点亮在社区的每一天
去签到