二叉树的实现----C语言数据结构

目录

  • 1.二叉树的概念
  • 2.二叉树所需要的实现的功能
  • 3.二叉树的实现----各个功能的需求实现
    • 1.通过数组构建二叉树----以递归的形式
    • 2.二叉树的遍历
      • 1.1 前序遍历
      • 1.2 中序遍历
      • 1.3 后序遍历
      • 1.4 ==__层序遍历__==
    • 3. 二叉树的节点个数
    • 4. 二叉树的叶子节点的个数
    • 5. 二叉树中查找值为k的结点(返回结点)
    • 6. 二叉树中第k层的结点个数
    • 7. 判断二叉树是否是完全二叉树
    • 8. 二叉树的销毁
    • 9. 二叉树的深度/高度
  • 4. 代码汇总

1.二叉树的概念

一棵二叉树是结点的一个有限集合,该集合:

  1. 或者为空

  2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成
    从图可以看出:
    二叉树的实现----C语言数据结构_第1张图片

  3. 二叉树不存在度大于2的结点

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

关于更详细的二叉树的性质:二叉树的概念于性质(转载)

2.二叉树所需要的实现的功能

#pragma once
#include
#include
#include

typedef char BTDataType;

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

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树,#表示NULL
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);

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);

3.二叉树的实现----各个功能的需求实现

1.通过数组构建二叉树----以递归的形式

BTNode* Newnode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}

用一个数组构建二叉树,将数组的每一个元素,像前序遍历一样,第一个赋予根节点,第二个根节点的左子树,第三个根结点的右子树,以此类推:值先给父节点,再给父节点的左子树,再给父节点的右子…递归直到数组的所有值都遍历

如下图
二叉树的实现----C语言数据结构_第2张图片

//这里n代表数组的大小
// *pi是代表当前数组的下标
// "#"代表NULL
BTNode* BinaryTreeCreate(BTDataType* a,int n, int* pi)
{
	//在给值的时候,判断若为NULL,那么返回
	if (a[*pi] == '#' || (*pi) >= n)
	{
		(*pi)++;
		//printf("NULL");
		return NULL;
	}
	//在赋值不为空的情况下,再创建新的节点
	//创造新结点
	BTNode* dst = Newnode(a[*pi]);
//	printf("%c",dst->data);
//在创建成功新节点后,需要将下标的位置加一
	(*pi)++;
	dst->left = BinaryTreeCreate(a, n, pi);
	dst->right = BinaryTreeCreate(a, n, pi);
	return dst;
}

这里下标用的指针,是因为函数栈帧,在递归的时候,若传的是int pi ,那么这个值不会受上一个函数中的pi++影响,pi的值不会改变.

2.二叉树的遍历

二叉树的遍历中,前,中,后序的思路差不多

1.1 前序遍历

二叉树的实现----C语言数据结构_第3张图片

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

1.2 中序遍历

二叉树的实现----C语言数据结构_第4张图片

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

1.3 后序遍历

二叉树的实现----C语言数据结构_第5张图片

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

1.4 层序遍历

层序遍历就是从上往下,由第一层遍历到第二层遍历到……第n层

层序遍历的实现需要用到队列.因为队列的特点,可以让第一层的绝对比第二层的先出也就是先遍历
思路:第一层的结点进队列,第一层的结点出队列,若当前队列为空代表结点已全出,若不为空,则将下一层的结点进队列
二叉树的实现----C语言数据结构_第6张图片

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	//先插入root的值
	QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		QDate front = QueueFront(&q);
		QueuePop(&q);

		printf("%c ", front->data);

		if (front->left)
			QueuePush(&q, front->left);
		if (front->right)
			QueuePush(&q, front->right);
	}
	QueueDestroy(&q);
}

3. 二叉树的节点个数

//二叉树的结点个数
int BinaryTreeSize(BTNode* root)
{
	//若为空树,也就是一开始就是空树,或者结点的左右子树传参后,也是空
	//那么结点个数便为0
	if (root == NULL)
	{
		return 0;
	}
	//诺此节点没有子节点,这个结点(相当于只有一个父节点的树,也就是一个结点的树)为1
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	//总结点个数为 左子树的结点数加右子树的结点数,再加上初始结点(父节点)
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

4. 二叉树的叶子节点的个数

// 二叉树叶子节点个数
//思路和总结点个数差不多
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);
}

5. 二叉树中查找值为k的结点(返回结点)

实现二叉树中寻找k值的结点,许需要返回k值这个结点的指针
这里采用前序遍历来查找k值,因为对比中序,后序遍历,前序遍历在找到值便可以直接返回,不再递归
而中序后序,找到k值仍会继续递归,而错过k值的结点
思路:结点(根节点)是否是k值,若不是,找左子树中是否是k值,若不是,找右子树中是否是k值……(递归)
直到遍历整个树,若整棵树都没有k值,那么返回NULL

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	//这俩段是重点,设接收点来接收左子树为x值的点(递归),没有就遍历右子树
	BTNode* left=BinaryTreeFind(root->left, x);
	if (left != NULL) return left;

	BTNode* right=BinaryTreeFind(root->right, x);
	if (right != NULL) return right;

	//若整个树都没有x的值,传空
	return NULL;
}

6. 二叉树中第k层的结点个数

这个问题可以用到分治算法,把问题细分化,要计算第k层的结点数,可以化解为小问题:根节点中的左子树的第k-1层和右子树的第k-1层的结点数之和 ,依次递归
最后递归到子树只有一层的时候(也就是递归到根节点的第k层),返回(从下往上返回)
如下图
在这里插入图片描述

// 二叉树第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);
}

7. 判断二叉树是否是完全二叉树

判断是否是完全二叉树首先要知道完全二叉树的特点:
叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。需要注意的是,满二叉树肯定是完全二叉树,而完全二叉树不一定是满二叉树。
算法思路
判断一棵树是否是完全二叉树的思路 :
1>如果树为空,则直接返回错
2>如果树不为空:层序遍历二叉树
2.1>如果一个结点左右孩子都不为空,则pop该节点,将其左右孩子入队列;
2.1>如果遇到一个结点,左孩子为空,右孩子不为空,则该树一定不是完全二叉树;
2.2>如果遇到一个结点,左孩子不为空,右孩子为空;或者左右孩子都为空,且则该节点之后的队列中的结点都为叶子节点,该树才是完全二叉树,否则就不是完全二叉树

这里用层序遍历来实现,也就是若遇到空结点了,而继续遍历仍然有值,那么就不是完全二叉树
二叉树的实现----C语言数据结构_第7张图片

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		QDate front = QueueFront(&q);
		QueuePop(&q);

		if (front == NULL)
		{
			break;
		}

		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}

	while (!QueueEmpty(&q))
	{
		QDate front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueueDestroy(&q);
			return 1;
		}
	}
	return 0;
}

8. 二叉树的销毁

在二叉树的销毁中,采用后序遍历是最佳的,因为对比中序和前序,当遍历到此结点时,销毁此节点,会丢失此节点的左右子树结点,不利于遍历到下一个结点.(当然可以设置临时变量记录此节点也是可以实现的)

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));
	free(*root);
	*root = NULL;
}

9. 二叉树的深度/高度

这里还是分治思想,实现二叉树的高度,可以分解为实现二叉树子树的高度加一

int BinaryTreeHeight(BTNode* root)
{
	if(root==NULL)
	return 0;
	//return BinaryTreeHeight(root->left)>BinaryTreeHeight(root->right) ? BinaryTreeHeight(root->left)+1:BinaryTreeHeight(root->right)+1;
	return fmax(BinaryTreeHeight(root->left),BinaryTreeHeight(root->right)+1;
}

这里注释中的语句可以现实,但是有冗余的处理

4. 代码汇总

以下是功能实现的所有代码
头文件包含

#pragma once
#include
#include
#include

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);

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);

队列头文件

#pragma once
#include
#include
#include
// 链式结构:表示队列 
typedef struct BinaryTreeNode* QDate;

typedef struct QListNode
{
	struct QListNode* next;
	QDate val;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* front;
	QNode* rear;
	int size;
}Queue;

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

binary的c文件

#define _CRT_SECURE_NO_WARNINGS

#include"Binary_tree.h"
#include"queue.h"

BTNode* Newnode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}
BTNode* BinaryTreeCreate(BTDataType* a,int n, int* pi)
{
	if (a[*pi] == '#' || (*pi) >= n)
	{
		(*pi)++;
		//printf("NULL");
		return NULL;
	}
	//创造新结点
	BTNode* dst = Newnode(a[*pi]);
//	printf("%c",dst->data);
	(*pi)++;
	dst->left = BinaryTreeCreate(a, n, pi);
	dst->right = BinaryTreeCreate(a, n, pi);
	return dst;
}

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

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

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

//二叉树的结点个数
int BinaryTreeSize(BTNode* root)
{
	//若为空树  为零
	if (root == NULL)
	{
		return 0;
	}
	//诺此节点没有子节点,这个结点(相当于只有一个父节点的树,也就是一个结点的树)为1
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	//总结点个数为 左子树的结点数加右子树的结点数,再加上初始结点(父节点)
	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;
	}
	//左子树叶子结点加右子树叶子节点树
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	//这俩段是重点,设接收点来接收左子树为x值的点(递归),没有就遍历右子树
	BTNode* left=BinaryTreeFind(root->left, x);
	if (left != NULL) return left;

	BTNode* right=BinaryTreeFind(root->right, x);
	if (right != NULL) return right;

	//若整个树都没有x的值,传空
	return NULL;
}

// 二叉树第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);
}

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));
	free(*root);
	*root = NULL;
}

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	//先插入root的值
	QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		QDate front = QueueFront(&q);
		QueuePop(&q);

		printf("%c ", front->data);

		if (front->left)
			QueuePush(&q, front->left);
		if (front->right)
			QueuePush(&q, front->right);
	}
	QueueDestroy(&q);
}

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		QDate front = QueueFront(&q);
		QueuePop(&q);

		if (front == NULL)
		{
			break;
		}

		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}

	while (!QueueEmpty(&q))
	{
		QDate front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueueDestroy(&q);
			return 1;
		}
	}
	return 0;
}

binarytree的测试代码

#define _CRT_SECURE_NO_WARNINGS

#include"Binary_tree.h"
#include"queue.h"
//typedef char BTDataType;
//
//typedef struct BinaryTreeNode
//{
//	BTDataType data;
//	struct BinaryTreeNode* left;
//	struct BinaryTreeNode* right;
//}BTNode;

void test1()
{
	BTDataType ma[18] = { "ABD##E#H##CF##G##" };
	int n = sizeof(ma) / sizeof(ma[0]);
	int pi = 0;
	BTNode* head = BinaryTreeCreate(ma,n,&pi);


	printf("%c\n",head->data);
	//BinaryTreePrevOrder(head);
	//BinaryTreeInOrder(head);
	BinaryTreePostOrder(head);
	//int size = BinaryTreeSize(head);
	int size = BinaryTreeLeafSize(head);
	BTNode* s1 = BinaryTreeFind(head, 'D');
	int k = BinaryTreeLevelKSize(head, 5);

	printf("%d ", size);
	printf("\n");
	BinaryTreeLevelOrder(head);
	int i = BinaryTreeComplete(head);
	printf("%d", i );
	BinaryTreeDestory(&head);
}

int main()
{
	test1();


	return 0;
}

队列的c文件

#define _CRT_SECURE_NO_WARNINGS

#include"queue.h"

void QueueInit(Queue* head)
{
	assert(head);
	head->front = NULL;
	head->rear = NULL;
	head->size = 0;
}

void QueuePush(Queue* head, QDate x)
{
	assert(head);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	newnode->next = NULL;
	newnode->val = x;
	//若为空栈,则新节点也赋予头 rear
	if (head->rear == NULL)
	{
		head->front = head->rear = newnode;
	}
	else
	{
		head->rear->next = newnode;
		head->rear = newnode;
	}
	head->size++;
}

void QueuePop(Queue* head)
{
	assert(head);
	assert(head->front != NULL);
	QNode* cur = head->front->next;
	free(head->front);
	head->front = cur;
	if (head->front == NULL)
		head->rear = NULL;
	head->size--;
}

QDate QueueFront(Queue* head)
{
	assert(head);
	assert(head->size > 0);
	return head->front->val;
}

QDate QueueBack(Queue* head)
{
	assert(head);
	assert(head->front != NULL);
	return head->rear->val;
}

int QueueSize(Queue* head)
{
	assert(head);
	return head->size;
}

bool QueueEmpty(Queue* head)
{
	assert(head);
	return head->size == 0;
}

void QueueDestroy(Queue* head)
{
	assert(head);
	QNode* cur = head->front;//即使当空队列的时候也可以
	while (cur)
	{
		QNode* tem = cur->next;
		free(cur);
		cur = tem;
	}
	head->front = NULL;
	head->rear = NULL;
	head->size = 0;
}

你可能感兴趣的:(数据结构,数据结构,c语言,开发语言)