一棵二叉树是结点的一个有限集合,该集合:
关于更详细的二叉树的性质:二叉树的概念于性质(转载)
#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);
BTNode* Newnode(BTDataType x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
node->data = x;
node->left = NULL;
node->right = NULL;
return node;
}
用一个数组构建二叉树,将数组的每一个元素,像前序遍历一样,第一个赋予根节点,第二个根节点的左子树,第三个根结点的右子树,以此类推:值先给父节点,再给父节点的左子树,再给父节点的右子…递归直到数组的所有值都遍历
//这里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的值不会改变.
二叉树的遍历中,前,中,后序的思路差不多
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);
}
层序遍历就是从上往下,由第一层遍历到第二层遍历到……第n层
层序遍历的实现需要用到队列.因为队列的特点,可以让第一层的绝对比第二层的先出也就是先遍历
思路:第一层的结点进队列,第一层的结点出队列,若当前队列为空代表结点已全出,若不为空,则将下一层的结点进队列
// 层序遍历
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 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;
}
// 二叉树叶子节点个数
//思路和总结点个数差不多
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);
}
实现二叉树中寻找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;
}
这个问题可以用到分治算法,把问题细分化,要计算第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);
}
判断是否是完全二叉树首先要知道完全二叉树的特点:
叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。需要注意的是,满二叉树肯定是完全二叉树,而完全二叉树不一定是满二叉树。
算法思路
判断一棵树是否是完全二叉树的思路 :
1>如果树为空,则直接返回错
2>如果树不为空:层序遍历二叉树
2.1>如果一个结点左右孩子都不为空,则pop该节点,将其左右孩子入队列;
2.1>如果遇到一个结点,左孩子为空,右孩子不为空,则该树一定不是完全二叉树;
2.2>如果遇到一个结点,左孩子不为空,右孩子为空;或者左右孩子都为空,且则该节点之后的队列中的结点都为叶子节点,该树才是完全二叉树,否则就不是完全二叉树
这里用层序遍历来实现,也就是若遇到空结点了,而继续遍历仍然有值,那么就不是完全二叉树
// 判断二叉树是否是完全二叉树
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;
}
在二叉树的销毁中,采用后序遍历是最佳的,因为对比中序和前序,当遍历到此结点时,销毁此节点,会丢失此节点的左右子树结点,不利于遍历到下一个结点.(当然可以设置临时变量记录此节点也是可以实现的)
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
if (*root == NULL)
{
return;
}
BinaryTreeDestory(&((*root)->left));
BinaryTreeDestory(&((*root)->right));
free(*root);
*root = NULL;
}
这里还是分治思想,实现二叉树的高度,可以分解为实现二叉树子树的高度加一
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;
}
这里注释中的语句可以现实,但是有冗余的处理
以下是功能实现的所有代码
头文件包含
#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;
}