【数据结构】二叉树的实现
创始人
2024-03-05 15:14:53
0

文章目录

  • 一、二叉树的概念
  • 二、特殊的二叉树
  • 三、二叉树的性质
  • 四、二叉树的存储结构
  • 五、二叉树链式结构实现
    • (1)创建结构体
    • (2)具体函数实现及实现
      • 1.0 二叉树的构建
      • 1.1 二叉树的销毁
      • 1.2 二叉树节点个数
      • 1.3 二叉树叶子结点个数
      • 1.4 二叉树第k层节点个数
      • 1.5 二叉树查找值为x的节点
      • 1.6 二叉树的高度
      • 1.7 二叉树前序遍历
      • 1.8 二叉树中序遍历
      • 1.9 二叉树后序遍历
      • 2.0 层序遍历
      • 2.1 判断二叉树是否是完全二叉树
    • (3)二叉树实现代码
      • (1)Queue.c
      • (2)Queue.h
      • (3)test.c
      • (4)BinaryTree.h
      • (5)BinaryTree.c
    • (4)二叉树测试结果

一、二叉树的概念

一棵二叉树是结点的一个有限集合,该集合分为两点: 一是为空和二是由一个根节点加上两棵别称为左子树和右子树的二叉树组成从图上看出有2个性质:

  1. 二叉树不存在度大于2的结点
  2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
    在这里插入图片描述

对于任意的二叉树都是由以下几种情况复合而成的:
在这里插入图片描述

二、特殊的二叉树

1、 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是 说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。

2、完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。要注意的是满二叉树是一种特殊的完全二叉树。

如图:在这里插入图片描述

三、二叉树的性质

二叉树数的性质有五点:

  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) ( 是log以2 为底,n+1为对数)
  5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对 于序号为i的结点有:
    若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
    若2i+1=n否则无左孩子
    若2i+2=n否则无右孩子

四、二叉树的存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
1、顺序存储:
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
在这里插入图片描述
2、链式存储
二叉树的链式存储结构:是用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,这只讲二叉链。
在这里插入图片描述

五、二叉树链式结构实现

(1)创建结构体


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

先使用typedef int BTDataType,是为了方便改类型,在结构体里创建3个成员变量,BTDataType a,表示结点数据。struct BinaryTreeNode* left表示存储结点左孩子的地址,struct BinaryTreeNode* right表示存储结点右孩子的地址。

(2)具体函数实现及实现

1.0 二叉树的构建

BTNode* BuyBTNode(BTDataType x)//二叉树的构建
{BTNode* node = (struct BTNode*) malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");exit(-1);}node->data = x;node->left = NULL;node->right = NULL;return node;
}

二叉树构建也就是为每一个结点申请空间,用malloc开辟,地址放在node变量中,如果改指针为空就exit,接着把数据值放到结点数据里,把2它的左右孩子都置为空。

1.1 二叉树的销毁

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

二叉树销毁根节点为空就不用销毁,反之,先递归释放左子树,再递归释放右子树,最后释放结点,如果先释放结点,会找不到左右子树的地址。

1.2 二叉树节点个数


int BinaryTreeSize(BTNode* root)// 二叉树节点个数
{if (root == NULL){return 0;}return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;}

求二叉树结点个数需要递归左子树和右子树,去建立栈帧,直到它们的子树为空返回0,之后再加1,表示记录一个结点,然后从下到上开始return返回,栈帧逐层销毁,最终返回结点个数。

1.3 二叉树叶子结点个数

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

求叶子结点个数,也就是求同时没有左右孩子的结点,思路还是递归左子树和右子树,之和即为最终的叶子节点个数。如果结点为空直接返回0,如果它们的左右孩子都为空,则表示它是叶子结点,返回1,最后递归返回总个数。

1.4 二叉树第k层节点个数

int BinaryTreeLevelKSize(BTNode* root, int k)// 二叉树第k层节点个数
{if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

求第k层个数,从二叉树结构可以发现,如果k=1,他自己就是第k层,如果k>1,第k层就等于左子树k-1层个数加右子树第k-1层个数,相对位置往下走,层层递进,一直减到1,就是第k层。同样递归左右子树,最后返回最终第k层结点个数。

1.5 二叉树查找值为x的节点

struct BinaryTreeNode* BinaryTreeFind(BTNode* root, BTDataType x)// 二叉树查找值为x的节点
{if (root == NULL){return NULL;}if (root->data == x){return root;}struct BinaryTreeNode* ret1 = BinaryTreeFind(root->left, x);if (ret1)return ret1;struct BinaryTreeNode* ret2 = BinaryTreeFind(root->right, x);if (ret2)return ret2;}

二叉树查找,如果结点为空,返回空,如果找到直接返回结点指针,没找到继续分别递归左右子树,为了防止返回出现随机值,这里需要接收一下指针,如果找到返回该结点指针。

1.6 二叉树的高度

int BinaryTreeHeight(BTNode* root)//二叉树的高度
{if (root == NULL){return 0;}int leftheight = BinaryTreeHeight(root->left);int rightheight = BinaryTreeHeight(root->right);return leftheight > rightheight ? leftheight + 1:rightheight + 1;}

求二叉树高度思路把问题还是分解为子问题,结点高度等于左子树高度和右子树高度大的那个一个加1,因为高度是最长的那条路径,分别递归左子树和右子树去比较,如果都为0,任取一个加1。最终返回最终高度。

1.7 二叉树前序遍历

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

前序遍历更为简单,如果为空直接返回,接着打印根数据,再递归左子树,再递归右子树。

1.8 二叉树中序遍历

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

中序遍历,如果为空直接返回,先递归左子树,再打印根数据,再递归右子树。

1.9 二叉树后序遍历

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

中序遍历,如果为空直接返回,先递归左子树,再打印根数据,再递归右子树。

2.0 层序遍历

void BinaryTreeLevelOrder(BTNode* root)// 层序遍历
{Queue q;QueueInit(&q);if (root){QueuePush(&q, root);}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);printf("%d ", front->data);QueuePop(&q);if (front->left){QueuePush(&q, front->left);}if (front->right){QueuePush(&q, front->right);}}printf("\n");QueueDestroy(&q);
}

层序遍历在这用队列去实现,队列里面存地址。如果根节点不为空,就先把这个结点地址导入进去。之后用while循环来控制终止,队列不为空就进去,然后先把上次导入进去的队列头部数据保存起来放到front指针变量,便于删除在队列里删除它,还不影响导入它的左右孩子,左右孩子不为空则导入。最后释放队列。

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

bool BinaryTreeComplete(BTNode* root)// 判断二叉树是否是完全二叉树
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL){break;}else{QueuePush(&q, front->left);QueuePush(&q, front->right);}}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front != NULL){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;}

判断是否是完全二叉树的思路:是同样是用队列去实现,如果从队列出,遇到结点为空,就去判断后面的是否为空,后面只要出现一个非空就不是完全二叉树,后面全为空就是二叉树。
1、如果根结点不为空,就先导入队列中,接着同样是用while循环,里面是导入数据即结点地址,先把队列头保存起来,再pop掉,if语句为真,停止,说明遇到空了,否则继续导入数据包括空也导入进去。
2、接着再用whil循环,出到空后,继续往后出数。只要找到一个非空就返回false,直到循环结束,返回真,说明都是空。

(3)二叉树实现代码

(1)Queue.c

#include"Queue.h"
void QueueInit(Queue* qq)//队列初始化
{assert(qq);qq->head = qq->tail = NULL;qq->size = 0;}void QueuePush(Queue* qq, QDataType x)//入队列
{assert(qq);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");}newnode->data = x;newnode->next = NULL;if (qq->tail == NULL){qq->head = qq->tail = newnode;}else{qq->tail->next = newnode;qq->tail = qq->tail->next;}qq->size++;
}void QueuePop(Queue* qq)//出队列
{assert(qq);assert(!QueueEmpty(qq));if (qq->head->next == NULL){free(qq->head);qq->head = qq->tail = NULL;}else{QNode* del = qq->head;qq->head = qq->head->next;free(del);}qq->size--;}QDataType QueueFront(Queue* qq)//取队列首元素
{assert(qq);assert(!QueueEmpty(qq));return qq->head->data;}QDataType QueueBack(Queue* qq)//取队列尾元素
{assert(qq);assert(!QueueEmpty(qq));return qq->tail->data;}int QueueSize(Queue* qq)//返回队列个数
{assert(qq);return qq->size;
}bool QueueEmpty(Queue* qq)//判断是否为空
{assert(qq);return qq->head == NULL &&qq->tail == NULL;}void QueueDestroy(Queue* qq)//销毁队列
{assert(qq);QNode* cur = qq->head;while (cur){QNode* del = cur;cur = cur->next;free(del);}qq->head = qq->tail = NULL;qq->size = 0;
}

(2)Queue.h

#pragma once
#include
#include
#include
#includestruct BinaryTreeNode;
typedef struct BinaryTreeNode* QDataType;typedef struct QueueNode
{QDataType data;struct QueueNode* next;
}QNode;
typedef struct Queue
{QNode* head;QNode* tail;int size;
}Queue;
void QueueInit(Queue* qq);//队列初始化void QueuePush(Queue* qq,QDataType x);//入队列void QueuePop(Queue* qq);//出队列QDataType QueueFront(Queue* qq);//取队列首元素QDataType QueueBack(Queue* qq);//取队列尾元素int QueueSize(Queue* qq);//返回队列个数bool QueueEmpty(Queue* qq);//判断是否为空void QueueDestroy(Queue* qq);//销毁队列

(3)test.c

#include"BinaryTree.h"
int main()
{BTNode* n1 = BuyBTNode(1);BTNode* n2 = BuyBTNode(2);BTNode* n3 = BuyBTNode(3);BTNode* n4 = BuyBTNode(4);BTNode* n5 = BuyBTNode(5);BTNode* n6 = BuyBTNode(6);BTNode* n7 = BuyBTNode(7);n2->right = n7;n1->left = n2;n1->right = n4;n2->left = n3;n4->left = n5;n4->right = n6;BinaryTreePrevOrder(n1);//前序遍历printf("\n");BinaryTreeInOrder(n1);//中序遍历printf("\n");BinaryTreePostOrder(n1);//后序遍历printf("\n");printf("TreeSize:%d\n", BinaryTreeSize(n1));printf("TreeLeafSize:%d\n", BinaryTreeLeafSize(n1));printf("TreeHeight:%d\n", BinaryTreeHeight(n1));printf("TreeKLevelSize:%d\n", BinaryTreeLevelKSize(n1, 3));printf("TreeFind:%p\n", BinaryTreeFind(n1, 7));BinaryTreeLevelOrder(n1);return 0;}

(4)BinaryTree.h

#pragma once
#include
#include
#includetypedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;#include"Queue.h"BTNode* BuyBTNode(BTDataType x);//二叉树的构建void BinaryTreeDestory(BTNode** root);//二叉树的销毁int BinaryTreeSize(BTNode* root);// 二叉树节点个数int BinaryTreeLeafSize(BTNode* root);//二叉树叶子结点个数int BinaryTreeLevelKSize(BTNode* root, int k);// 二叉树第k层节点个数BTNode* BinaryTreeFind(BTNode* root, BTDataType x);// 二叉树查找值为x的节点int BinaryTreeHeight(BTNode* root);//二叉树的高度void BinaryTreePrevOrder(BTNode* root);// 二叉树前序遍历 void BinaryTreeInOrder(BTNode* root);// 二叉树中序遍历void BinaryTreePostOrder(BTNode* root);// 二叉树后序遍历void BinaryTreeLevelOrder(BTNode* root);// 层序遍历bool BinaryTreeComplete(BTNode* root);// 判断二叉树是否是完全二叉树

(5)BinaryTree.c

#include"BinaryTree.h"BTNode* BuyBTNode(BTDataType x)//二叉树的构建
{BTNode* node = (struct BTNode*) malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");exit(-1);}node->data = x;node->left = NULL;node->right = NULL;return node;
}void BinaryTreeDestory(BTNode* root)//二叉树的销毁
{if (root){BinaryTreeDestory(root->left);BinaryTreeDestory(root->right);free(root);}}int BinaryTreeSize(BTNode* root)// 二叉树节点个数
{if (root == NULL){return 0;}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);
}int BinaryTreeLevelKSize(BTNode* root, int k)// 二叉树第k层节点个数
{if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}struct BinaryTreeNode* BinaryTreeFind(BTNode* root, BTDataType x)// 二叉树查找值为x的节点
{if (root == NULL){return NULL;}if (root->data == x){return root;}struct BinaryTreeNode* ret1 = BinaryTreeFind(root->left, x);if (ret1)return ret1;struct BinaryTreeNode* ret2 = BinaryTreeFind(root->right, x);if (ret2)return ret2;}int BinaryTreeHeight(BTNode* root)//二叉树的高度
{if (root == NULL){return 0;}int leftheight = BinaryTreeHeight(root->left);int rightheight = BinaryTreeHeight(root->right);return leftheight > rightheight ? leftheight + 1:rightheight + 1;}void BinaryTreePrevOrder(BTNode* root)// 二叉树前序遍历 
{if (root == NULL){printf("NULL ");return;}printf("%d ", root->data);BinaryTreePrevOrder(root->left);BinaryTreePrevOrder(root->right);
}void BinaryTreeInOrder(BTNode* root)// 二叉树中序遍历
{if (root == NULL){printf("NULL ");return;}BinaryTreeInOrder(root->left);printf("%d ", root->data);BinaryTreeInOrder(root->right);
}void BinaryTreePostOrder(BTNode* root)// 二叉树后序遍历
{if (root == NULL){printf("NULL ");return;}BinaryTreePostOrder(root->left);BinaryTreePostOrder(root->right);printf("%d ", root->data);
}void BinaryTreeLevelOrder(BTNode* root)// 层序遍历
{Queue q;QueueInit(&q);if (root){QueuePush(&q, root);}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);printf("%d ", front->data);QueuePop(&q);if (front->left){QueuePush(&q, front->left);}if (front->right){QueuePush(&q, front->right);}}printf("\n");QueueDestroy(&q);
}bool BinaryTreeComplete(BTNode* root)// 判断二叉树是否是完全二叉树
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL){break;}else{QueuePush(&q, front->left);QueuePush(&q, front->right);}}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front != NULL){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;}

(4)二叉树测试结果

在这里插入图片描述

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...