数据结构——二叉树
目录
1、二叉树的概念及结构
1.1 概念
一棵二叉树是结点的一个有限集合,该集合:
1. 可以 由一个根结点加上两棵别称为左子树和右子树的二叉树组成
2. 也可以 为空
注意:
1. 二叉树不存在度大于2的结点
2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
3. 对于任意的二叉树都是由以下几种情况复合而成的:
1.2 特殊的二叉树
1. 满二叉树:如果一个二叉树的层数为K(根节点为第1层),且节点总数是2^k - 1 ,则它就是满二叉树。
2. 完全二叉树:前K - 1层是满二叉树,第K层,从左到右,是连续节点,且不间断
1.3 二叉树的性质
1. 若根节点的层数为1,则一棵非空二叉树的第 k 层上最多有2^(k -1)个节点
节点总数最多为2^k - 1个节点
2. 对任何一棵二叉树,如果叶子节点个数为N,度为2的节点个数为M ,则有 N = M + 1
叶 = 度2 + 1
3. 若根节点的层数为1,具有n个节点的满二叉树的深度,h = log(n + 1),以2为底,n+1为对数
4. 对于具有n个节点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从0开始编号,则:
第 i 个节点,其双亲节点 (i - 1) / 2,左孩子 2 * i + 1,右孩子 2 * i + 2
1.4 二叉树的存储结构
1.4.1 顺序存储
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。
1.4.2 链式存储
链式结构分为二叉链和三叉链,当前学习中一般都是二叉链,后面学到高阶数据结构如红黑树等会用到三叉链。
typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
struct BinTreeNode* left; // 指向当前结点左孩子
struct BinTreeNode* right; // 指向当前结点右孩子
BTDataType data; // 当前结点值域
}
// 三叉链
struct BinaryTreeNode
{
struct BinTreeNode* parent; // 指向当前结点的双亲
struct BinTreeNode* left; // 指向当前结点左孩子
struct BinTreeNode* right; // 指向当前结点右孩子
BTDataType data; // 当前结点值域
};
2、二叉树的顺序结构及实现
普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
堆+堆排序+topK问题-CSDN博客 我的这篇博客里有详细介绍
3、二叉树链式结构的实现
在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。
由于现在对二叉树结构掌握还不够深入,为了降低学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,待二叉树结构了解的差不多时,再回来研究二叉树真正的创建方式。
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
BTNode* BuyNode(BTDataType x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
perror("BuyNode()::malloc()");
return NULL;
}
node->_data = x;
node->_left = node->_right = NULL;
return node;
}
BTNode* CreatBinaryTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode* node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
node1->_left = node2;
node1->_right = node4;
node2->_left = node3;
node4->_left = node5;
node4->_right = node6;
return node1;
}
3.1 BinaryTree.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <vld.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
BTNode* BuyNode(BTDataType x);
// 建树
BTNode* CreatBinaryTree();
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 二叉树的高度
int TreeHeight(BTNode* root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
3.2 BinaryTree.c
#include "BinaryTree.h"
#include "Queue.h"
BTNode* BuyNode(BTDataType x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
perror("BuyNode()::malloc()");
return NULL;
}
node->_data = x;
node->_left = node->_right = NULL;
return node;
}
BTNode* CreatBinaryTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode* node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
node1->_left = node2;
node1->_right = node4;
node2->_left = node3;
node4->_left = node5;
node4->_right = node6;
return node1;
}
void BinaryTreeDestory(BTNode** root)// 二叉树销毁
{
assert(root);
if (*root == NULL)
return;
BinaryTreeDestory(&(*root)->_left);
BinaryTreeDestory(&(*root)->_right);
free(*root);
}
void BinaryTreePrevOrder(BTNode* root)// 二叉树前序遍历
{
if (root == NULL)
return;
printf("%d ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
void BinaryTreeInOrder(BTNode* root)// 二叉树中序遍历
{
if (root == NULL)
return;
BinaryTreeInOrder(root->_left);
printf("%d ", root->_data);
BinaryTreeInOrder(root->_right);
}
void BinaryTreePostOrder(BTNode* root)// 二叉树后序遍历
{
if (root == NULL)
return;
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%d ", root->_data);
}
层序遍历二叉树,用队列存储(先进先出),出一个节点,带入其下一层的节点
pop,是pop链式队列的一个节点(里面存的是指向二叉树节点的指针)
pop掉指针,没有pop掉二叉树的节点
队列为空(节点都free了),不需要destroy,只有节点开了堆上的空间,Queue q是开在栈上的空间
void BinaryTreeLevelOrder(BTNode* root)// 层序遍历
{
if (root == NULL)
return;
Queue q;
QueueInit(&q);
QueuePush(&q, root);
//while (!QueueEmpty(&q))
//{
//BTNode* front = QueueFront(&q);
//QueuePop(&q);
//if (front != NULL)
//{
//printf("%d ", front->_data);
//QueuePush(&q, front->_left);
//QueuePush(&q, front->_right);
//}
//}//会把叶子节点的两个NULL,push,对于空节点,要push,pop
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%d ", front->_data);
if(front->_left)
QueuePush(&q, front->_left);
if (front->_right)
QueuePush(&q, front->_right);
}//不会把叶子节点的两个NULL,push,队列里只有非空的节点
}
用leftheight和rightheight,记录高度,不然底层的节点要重复执行非常多次
int TreeHeight(BTNode* root)// 二叉树的高度
{
if (root == NULL)
return 0;
int leftheight = TreeHeight(root->_left);
int rightheight = TreeHeight(root->_right);
return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
}
int BinaryTreeSize(BTNode* root)// 二叉树节点个数
{
return root == NULL ? 0 : 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);
}
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)// 二叉树查找值为x的节点
{
if (root == NULL)
return NULL;
if (root->_data == x)
return root;
BTNode* ret = BinaryTreeFind(root->_left, x);
if (ret)
return ret;
ret = BinaryTreeFind(root->_right, x);
if (ret)
return ret;
return NULL;
}
3.3 Queue.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//typedef BTNode* QDataType;//若要用BTNode*,要包含"BinaryTree.h",我认为结构体声明,作用域和全局变量一样,typedef,作用域只在当前个文件里
typedef struct BinaryTreeNode* QDataType;//存二叉树节点指针,
// 链式结构:表示队列
typedef struct QListNode
{
struct QListNode* _next;
QDataType _data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* _front;
QNode* _rear;
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
int QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);
3.4 Queue.c
#include "Queue.h"
// 初始化队列
void QueueInit(Queue* q)
{
assert(q);
q->_front = q->_rear = NULL;
q->size = 0;
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
assert(q);
QNode* tmp = (QNode*)malloc(sizeof(QNode));
if (tmp == NULL)
{
perror("QueueNode()::malloc()");
return;
}
tmp->_next = NULL;
tmp->_data = data;
if (q->size == 0)
{
q->_front = q->_rear = tmp;
}
else
{
q->_rear->_next = tmp;
q->_rear = tmp;
}
q->size++;
}
// 队头出队列
void QueuePop(Queue* q)
{
assert(q);
if (q->size == 0)
{
printf("队列已空\n");
return;
}
QNode* del = q->_front;
q->_front = q->_front->_next;
if (q->_front == NULL)
{
q->_rear = NULL;
}
free(del);
del = NULL;
q->size--;
}
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
assert(q);
assert(q->_front);
return q->_front->_data;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
assert(q);
assert(q->_rear);
return q->_rear->_data;
}
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
return q->size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q)
{
if (q->size == 0)
return 1;
else
return 0;
}
// 销毁队列
void QueueDestroy(Queue* q)
{
while (q->_front)
{
QNode* del = q->_front;
q->_front = q->_front->_next;
free(del);
}
q->_rear = NULL;
q->size = 0;
}
3.5 test(测试代码)
#include "BinaryTree.h"
int main()
{
BTNode* root = CreatBinaryTree();
printf("BinaryTreeSize:>%d\n", BinaryTreeSize(root));// 二叉树节点个数
printf("BinaryTreeLeafSize:>%d\n", BinaryTreeLeafSize(root));// 二叉树叶子节点个数
printf("BinaryTreeLevelKSize:>%d\n", BinaryTreeLevelKSize(root, 3));// 二叉树第k层节点个数
BTNode* ret = BinaryTreeFind(root, 4);// 二叉树查找值为x的节点
if (ret == NULL)
printf("BinaryTreeFind:> NoFind");
printf("BinaryTreeFind:>%d\n", ret->_data);
BinaryTreePrevOrder(root);// 前序遍历
printf("\n");
BinaryTreeInOrder(root);// 中序遍历
printf("\n");
BinaryTreePostOrder(root);// 后序遍历
printf("\n");
BinaryTreeLevelOrder(root);// 层序遍历
printf("\n");
printf("TreeHeight:>%d\n", TreeHeight(root));// 树的高度
BinaryTreeDestory(&root);// 二叉树销毁
return 0;
}
3.6 二叉树的创建
pi是下标i的地址,每次调用时,i要移动到下一个位置
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
if (a[*pi] == '#')//注意是字符'',不是字符串""
{
(*pi)++;
return NULL;
}
BTNode* root = BuyNode(a[(*pi)++]);
root->_left = BinaryTreeCreate(a, pi);
root->_right = BinaryTreeCreate(a,pi);
return root;
}
注意:若要运行,注意把前面
BinaryTree.c 中 遍历函数里的%d->%c,
BinaryTree.h 中 typedef int BTDataType;->typedef char BTDataType;
test.c 中 BTNode* ret = BinaryTreeFind(root, 'F');// 二叉树查找值为x的节点,x为字符
printf("BinaryTreeFind:>%c\n", ret->_data);
原文地址:https://blog.csdn.net/2302_80310672/article/details/142342939
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!