二叉树基础功能的实现

二叉树相关知识

在二叉树这一块我们需要大量用到递归的思想。递归即先递推再回归,不论是树的遍历还是树的销毁等,都需要用到递归的思想。

下面需要用到队列相关知识,先把队列要用到的函数放在这里

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1



#include <string.h>

typedef struct BinaryTreeNode* DataType;


//队列数据元素结构
typedef struct node
{
    DataType info;
    struct node* next;
}QueueData;

typedef struct queueRecord {
    QueueData* front, * rear;
}LINKQUEUE;

typedef struct queueRecord* PLinkQueue;

PLinkQueue createEmptyQueue_link()
{
    //创建一个空队列,实质:生成一个LINKQUEUE类型的结点,并给front 和 rear 成员赋值
    struct queueRecord* LQueue = (struct queueRecord*)malloc(sizeof(struct queueRecord));
    struct node* newnode = (struct node*)malloc(sizeof(struct node));
    newnode->next = NULL;
    LQueue->front = newnode;
    LQueue->rear = LQueue->front;
    return LQueue;

}

int isEmptyQueue_link(PLinkQueue  queue)
{ //判定队列是否为空,实质: 看队列的front指针是否为空,若为空,则队列为空

    if (queue->front->next == NULL)
    {
        return 1;
    }
    return 0;

}

void enQueue_link(DataType x, PLinkQueue queue)
{
    //将数据元素x插入队尾。实质:生成一个struct node类型的结点,并给相应成员赋值后插入队尾
    struct node* newnode = (struct node*)malloc(sizeof(struct node));
    newnode->info = x;
    newnode->next = queue->rear->next;
    queue->rear->next = newnode;
    queue->rear = newnode;

}



DataType deQueue_link(PLinkQueue Q)
{
    //出队,实质: 取出Q队列的队首结点,返回该结点的数据元素,并将该结点使用enQueue_link(QueueData *p,PLinkQueue Q)插入队尾
    QueueData* temp = Q->front->next;
    DataType ret = temp->info;
    Q->front->next = temp->next;
    free(temp);

    return ret;

}


用代码来实现二叉树,首先我们要对二叉树进行定义。

一颗二叉树需要有左节点和右节点,然后还有这个节点上的值。

定义二叉树
typedef struct BinaryTreeNode
{
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
	int val;
}BTNode;

然后我们需要对这个节点进行初始化,也就是创建这个节点

创建节点函数
BTNode* BuyNode(int x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));

	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newnode->val = x;
	newnode->left = NULL;
	newnode->right = NULL;
}

把这个节点的值传输函数中,然后赋值给他的val,左右子节点初始化为空

二叉树的销毁函数

销毁这个二叉树,肯定要先销毁他的左右子树,然后在销毁根节点。如果先销毁根节点,那么在销毁左右子树的时候就会出现问题。我们把这个函数写成这样:

void TreeDestory(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	TreeDestory(root->left);
	TreeDestory(root->right);
	free(root);//最后把根节点释放(销毁)
}

前序遍历:根节点->左子树->右子树

中序遍历:左子树->根节点->右子树

后序遍历:左子树->右子树->根节点

按照这个思想,我们现在来实现树的前中后序遍历:

前序遍历
//前序遍历
void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		//printf("NULL ");
		return;
	}
	printf("%d ", root->val);
	PrevOrder(root->left);
	PrevOrder(root->right);
}
中序遍历
//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		//printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->val);
	InOrder(root->right);
}
后序遍历
//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		//printf("NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->val);
}

遍历完了我们可以数出这个树的节点数,以下面这棵树为例子
在这里插入图片描述

当我想要求这棵树的节点数的时候,我从根节点下手,则有总结点数=根节点数(1)+左子树节点数+右子树节点数,如下图:
在这里插入图片描述

把这个问题继续分下去,左子树也是等于该子树的根节点(1)+左子树节点数+右子树节点数。同理,右边也一样。我们把这个问题无限细化,直到他的子树为空的时候。写出这个函数长下面这样:

求树的节点数
int TreeSize(BTNode* root)
{
	//如果这个节点是NULL,返回0,否则就是返回左边的加上右边的,再加上自己(1)
	return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

进一步,我们需要求出树的叶子结点数量(叶子结点:左右节点为空),和上面思路相似。改一下思路,叶子结点就是左右节点为空的节点,满足这个条件就返回1,为空返回0。写出这个函数:

叶子结点的数量
//叶子结点的数量
int TreeLeafSize(BTNode* root)
{
	if (root == NULL)\
	{
		return 0;
	}

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

	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

当我们想要求解第k层的节点数的时候,那么我们就一层一层往下走,每走一层k就减一,直到k为1的时候说明走到这一层了,把这个函数写出来:

第k层的节点数
//第k层的节点数
int TreeKLevel(BTNode* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}

	if (k == 1)
	{
		return 1;
	}

	return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);

}

查找就是遍历

查找值为x的节点
//查找值为x的节点
BTNode* TreeFind(BTNode* root, int x)
{
	if (root == NULL)
	{
		return NULL;
	}

	if (root->val == x)
	{
		return root;
	}
	BTNode* ret = NULL;
	//左边找
	ret = TreeFind(root->left, x);
	if (ret != NULL)
		return ret;
	//左边找不到找右边
	ret = TreeFind(root->right, x);
	if (ret != NULL)
		return ret;

	return NULL;
}
层序遍历

我们用队列来实现,每次出一个节点就把他的子节点入进去,如下:

先把1放进去,然后在1出来的时候把他的子节点放进去,为空就不放进去。

在这里插入图片描述

变成下面这样,然后把2放出来,他的子节点又进去
在这里插入图片描述

然后3变成队头,如下图:
在这里插入图片描述

以此类推,直到队列为空。

//层序遍历
void LevelOrder(BTNode* root)
{

	struct queueRecord* queue = createEmptyQueue_link();
	if (root)
	{
		enQueue_link(root, queue);
	}
	BTNode* cur = queue->front->next->info;
	//因为带了头结点,所以这里的判空是判断他的头结点的下一个是不是空
	while (!isEmptyQueue_link(queue))
	{
		//当前指针指向队列头结点的下一个的数据域
		cur = queue->front->next->info;
		if (cur->left)//这个节点的左边不为空就进去
		{
			enQueue_link(cur->left, queue);
		}
		if (cur->right)//同上
		{
			enQueue_link(cur->right, queue);
		}
		DataType result = deQueue_link(queue);//获得这个节点
		printf("%d ", result->val);//把这个节点的数据打印出来
	}
	
}
判断是否完全二叉树

我们用层序遍历,这里只需要把空也放进去,然后当发现空的时候判断后面还有没有空。画个图方便理解。
在这里插入图片描述
很明显这个不是一颗完全二叉树,当我们查找到第一个NULL时候,发现后面还有不是NULL的节点,由此判断这不是一颗完全二叉树。把这个函数写出来:

//判断是不是完全二叉树
int TreeComplete(BTNode* root)
{

	struct queueRecord* queue = createEmptyQueue_link();
	if (root)
	{
		enQueue_link(root, queue);
	}
	BTNode* cur = queue->front->next;
	//因为带了头结点,所以这里的判空是判断他的头结点的下一个是不是空
	while (!isEmptyQueue_link(queue))
	{
		//当前指针指向队列头结点的下一个的数据域
		cur = queue->front->next->info;
		if (cur == NULL)
		{
			break;
		}
		enQueue_link(cur->left, queue);
		enQueue_link(cur->right, queue);
		deQueue_link(queue);//把队头出队列
	}
	QueueData* judge = queue->front->next;
	while (judge)
	{
		if (judge->info)
		{
			return 0;
		}
		judge = judge->next;
	}
	return 1;
}

最后放上我用来测试的主函数,大家可以去试试

int main()
{
	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);
	node1->left = node2;
	node1->right = node3;
	node2->left = node4;
	node2->right = node5;
	node3->left = node6;
	node3->right = node7;
	node4->left = node8;
	node5->left = node9;

	PrevOrder(node1);
	printf("\n");

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

	PostOrder(node1);
	printf("\n");


	printf("treesize:%d\n", TreeSize(node1));

	printf("Tree 1 Level:%d\n", TreeKLevel(node1, 1));
	printf("Tree 2 Level:%d\n", TreeKLevel(node1, 2));
	printf("Tree 3 Level:%d\n", TreeKLevel(node1, 3));
	printf("Tree 4 Level:%d\n", TreeKLevel(node1, 4));
	LevelOrder(node1);

	
	printf("\nTreeComplete:%d", TreeComplete(node1));
	TreeDestory(node1);
	node1 = NULL;
	return 0;
}

不足之处希望大家多给我提出意见,我们一起进步!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值