数据结构之二叉树

本文详细介绍了树和二叉树的概念、结构,包括节点度、叶节点、父节点、子节点等基本概念,以及二叉树的特殊类型如满二叉树和完全二叉树。还探讨了树和二叉树的不同表示方法,如孩子兄弟表示法,以及二叉树的链式存储结构实现,包括前序遍历、中序遍历等操作的代码示例。
摘要由CSDN通过智能技术生成

1.树的概念及结构

1.1树的概念

         树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。树有一个特殊的结点,称为根结点,根节点没有前驱结点除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i<= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继因此,树是递归定义的。注意:树中的子树之间不能有交集,树中不能有环的存在。

1.2树的相关概念

        

  1. 节点的度:一个节点含有的子树的个数叫做节点的度。例如:上图中A的度为6.
  2. 叶节点或终端节点:度为零的节点称为叶节点。例如P,Q。
  3. 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点。
  4. 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点。
  5. 树的度:一颗树中,最大的节点的度称为这棵树的度。上图中的度就是A的度:6。
  6. 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层。
  7. 树的高度或深度:树中节点的最大层次。
  8. 森林:由m(m>0)棵互不相交的树的集合称为森林。

1.3树的表示

        树结构比较复杂了,要存储表示起来比较麻烦,既要保存值域,也要保存结点和结点之间的关系,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。我们这里就简单的了解其中最常用的孩子兄弟表示法。

typedef int DataType;
struct Node{
    struct Node * LeftChild;//左孩子
    struct Node * RightBrother;//右兄弟
    DataType Data;
}

2.二叉树的概念和结构

2.1二叉树的概念

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

  1. 或者为空
  2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成

从上图可以看出:

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

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

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

2.2特殊的二叉树

  1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。
  2.  完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树

2.3二叉树的性质

  1.  若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有 2^{i-1}个结点。
  2.  若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^{h}-1 。
  3.  对任何一棵二叉树, 如果度为0其叶结点个数为N0 , 度为2的分支结点个数为N2 ,则有N0= N2+1。
  4.  若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=\log (n+1) . (ps: \log (n+1)是log以2为底,n+1为对数)。
  5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:
    1.若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
    2. 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
    3. 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子

2.4二叉树的存储结构

            二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
        1. 顺序存储
            顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全           二叉树会有空间的浪费。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

        2.链式存储
            二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。           通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出           该结点左孩子和右孩子所在的链结点的存储地址 。

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

下面我们基于上述描述对二叉树的链式结构进行实现:(因为在二叉树的层序遍历和判断完全二叉树中用到了队列所以这里也展示队列的代码)

3.1头文件

二叉树:

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

typedef char BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType val;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;
#include"quene.h"
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root,int *k);
// 二叉树第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);
BTNode* BuyNode(BTDataType val);
BTNode* BinTree();

队列头文件:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include<assert.h>
typedef struct BinaryTreeNode* QDataType;
typedef struct QueueNode {
	QDataType val;
	struct QueueNode* next;
}QNode;
typedef struct Queue {
	QNode * phead;
	QNode * ptail;
	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 
bool QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

3.2源文件

二叉树源文件(.c文件):

#include"BinaryTree.h"

BTNode* BinaryTreeCreate(BTDataType* a,  int* pi) {
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
	BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
	if (newNode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	newNode->val = a[*pi];
	(*pi)++;
	newNode->left = BinaryTreeCreate(a,pi);
	newNode->right = BinaryTreeCreate(a, pi);
	return newNode;
}
void BinaryTreeDestory(BTNode** root) {
	if (*root == NULL)
	{
		return;
	}
	//后序遍历销毁
	BinaryTreeDestory(&(*root)->left);
	BinaryTreeDestory(&(*root)->right);
	free(*root);
	*root = NULL;
}
//前序遍历,根 左子树 右子树
void BinaryTreePrevOrder(BTNode* root) {
	if (root == NULL)
	{
		printf("N");
		return;
	}
	printf("%d", root->val);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}
void BinaryTreeInOrder(BTNode* root) {
	if (root == NULL)
	{
		printf("N");
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%d", root->val);
	BinaryTreeInOrder(root->right);
}
void BinaryTreePostOrder(BTNode* root) {
	if (root == NULL)
	{
		printf("N");
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%d", root->val);
}
int BinaryTreeSize(BTNode* root) {
	if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
//叶子节点个数
int BinaryTreeLeafSize(BTNode* root,int *k) {
	if (root != NULL&&root->left==NULL&&root->right==NULL)
	{
		(*k)++;
	}
	if (root != NULL)
	{
		 BinaryTreeLeafSize(root->left,k);
		 BinaryTreeLeafSize(root->right, k);
	}
	return *k;
}
int BinaryTreeLevelKSize(BTNode* root, int k) {
	assert(k > 0);
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->left,k-1) + BinaryTreeLevelKSize(root->right, k - 1);
}
BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {
	if (root == NULL)
	{
		return NULL;
	}
	if (root->val == x)
	{
		return root;
	}
	BTNode* tmp1 = BinaryTreeFind(root->left,x);
	BTNode* tmp2 = BinaryTreeFind(root->right,x);
	if (tmp1)
	{
		return tmp1;
	}
	if (tmp2)
	{
		return tmp2;
	}
	return NULL;
}
//层序遍历
void BinaryTreeLevelOrder(BTNode* root) {
	//把节点当做队列中的数据
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q,root);
	
	while (!QueueEmpty(&q))
	{
		BTNode* front = (BTNode*)QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			printf("%d",front->val);
			//根出队列,左右子树进队列
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
		else
		{
			printf("N");
		}
	}
	printf("\n");
	QueueDestroy(&q);
}
//判断是否是完全二叉树,借助层序遍历
bool BinaryTreeComplete(BTNode* root) {
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = (BTNode*)QueueFront(&q);
		QueuePop(&q);
		//遇到空直接跳出循环
		if (front == NULL)
		{
			break;
		}
		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}
	//如果是完全二叉树,之后便都是空:NNNNNN
	while (!QueueEmpty(&q))
	{
		BTNode* front = (BTNode*)QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	
	QueueDestroy(&q);
	return true;
}
//创建节点
BTNode* BuyNode(BTDataType val)
{
	BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
	if (newNode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	newNode->val = val;
	newNode->left = NULL;
	newNode->right = NULL;
	return newNode;
}
//构建二叉树
BTNode* BinTree() {
	BTNode* n1 = BuyNode(1);
	BTNode* n2 = BuyNode(2);
	BTNode* n3 = BuyNode(4);
	BTNode* n4 = BuyNode(3);
	BTNode* n5 = BuyNode(5);
	BTNode* n6 = BuyNode(6);
	//     n1
	//  n2    n3
	//n4 n5  n6
	n1->left = n2;
	n1->right = n3;
	n2->left = n4;
	n2->right = n5;
	n3->left = n6;
	
	return n1;
}

队列源文件(.c文件):

#include "quene.h"
// 初始化队列 
void QueueInit(Queue* q) {
	assert(q);
	q->phead = NULL;
	q->ptail = NULL;
	q->size = 0;
}
// 队尾入队列 
void QueuePush(Queue* q, QDataType data) {
	assert(q);
	QNode* ptmp = (QNode*)malloc(sizeof(QNode));
	if (ptmp == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	ptmp->val = data;
	ptmp->next = NULL;
	if (q->ptail)
	{
		q->ptail->next = ptmp;
		q->ptail = q->ptail->next;
	}
	else
	{
		q->phead = q->ptail = ptmp;
	}
	q->size++;
}
// 队头出队列 
void QueuePop(Queue* q) {
	assert(q);
	//队列为空
	assert(q->phead);
	//只有一个节点
	if (q->phead->next == NULL)
	{
		//free(q->phead);
		q->phead = q->ptail = NULL;
	}
	else
	{
		QNode* next = q->phead->next;
		//free(q->phead);
		q->phead = next;
	}
	q->size--;
}
// 获取队列头部元素 
QDataType QueueFront(Queue* q) {
	assert(q);
	assert(q->phead);
	return q->phead->val;
}
// 获取队列队尾元素 
QDataType QueueBack(Queue* q) {
	assert(q);
	assert(q->ptail);
	return q->ptail->val;
}
// 获取队列中有效元素个数 
int QueueSize(Queue* q) {
	assert(q);
	return q->size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* q) {
	assert(q);
	return q->size == 0;
}
// 销毁队列 
void QueueDestroy(Queue* q) {
	assert(q);
	QNode* cur = q->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	q->phead = NULL;
	q->ptail = NULL;
	q->size = 0;
}

二叉树的分享就到这里啦,后续会继续补充其他数据结构的!请大家多多点赞,你的点赞对我很重要!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值