文章目录
- 前言🚩
- 1、树?
- 2、树的相关概念
- 3、树的结构表示
- 4、二叉树
- 🚀、概念和结构
- 🎁、特殊二叉树
- 5、二叉树常用性质
- 6、二叉树的存储结构
- 🧩、顺序存储结构
- 🎨、链式存储结构
- 7、二叉树顺序结构的实现----堆
- 8、二叉树链式存储实现
- 9、部分代码递归解析
- 10、二叉树四种遍历
前言🚩
此次来了解的是不同于顺序表和链表的结构,顺序表和链表都是线性结构,而此次二叉树则是非线性结构的,从结构上来看相较于之前的要为复杂一些,接下来会讲到什么是二叉树?二叉树的相关性质和相关概念?代码结构实现?……
1、树?
树:一种非线性结构,由n个有限节点组成的具有一定层次关系的集合。从逻辑图上看类似一个倒过来的树。
这是网上一搜就可以看到的标准二叉树的结构。和下面逻辑结构相比较类似。
树的结构特点:
①在树形结构中,任何子树之间是不想交的;
②每颗子树的根节点只有一个前驱,可以有0个或者多个后继节点。
③一颗N个节点的树,有N-1条边。
2、树的相关概念
概念名称 | 具体含义 |
---|---|
节点的度 | 就是一个节点所含子树的个数 |
叶子节点/终端节点 | 度为0的节点(无后继的节点) |
双亲节点 | 若一个节点含有子节点,那么该节点就是父节点 |
孩子节点/子节点 | 一个节点含有的子树的根节点(父节点下面的节点) |
兄弟节点 | 具有相同父节点的节点 |
树的度 | 一棵树中,有最大的节点的度称为树的度、 |
树的深度/高度 | 就是该树有多少层 |
节点的祖先 | 从根节点到所经过分支上的所有节点 |
子孙 | 以某节点为根的子树中的任意节点,若是根节点,那么根节点之下的都是子孙 |
森林 | 有n(n>1)棵互不相交的树的集合(多颗不相交的树) |
3、树的结构表示
表示方法:孩子兄弟表示法
结构如图:
代码结构:
typedef int DataType;
struct Node
{
struct Node* _firstChild1; // 第一个孩子结点
struct Node* _pNextBrother; // 指向其下一个兄弟结点
DataType _data; // 结点中的数据域
};
4、二叉树
上面讲解的是树的结构,作为一种存储结构,在数据结构中的通常是写的是二叉树。
🚀、概念和结构
概念:是一棵拥有有限节点的集合;
结构特点:
①可以为空的树。
②具有一个根节点加上左子树和右子树两棵树组成;
③二叉树不存在度大于2的节点
④二叉树有左右两个孩子节点,具有一定顺序,次序不能颠倒,因此是有序树
以下都可以叫二叉树:
🎁、特殊二叉树
- 满二叉树
概念:在一棵二叉树中,只要该二叉树每一层的节点数量都达到最大值,则就是满二叉树。
结构图如下:
- 完全二叉树
概念:在一棵二叉树中,其最后一层的节点是从左到右是连续存在的,并且最后一层以上的都是最大节点数量,那么该树就是完全二叉树。
满二叉树是特殊的完全二叉树
满二叉树一定是完全二叉树,但完全二叉树不一定是满二叉树;
5、二叉树常用性质
- 在一棵二叉树中,以根节点为第一层为基准,则一棵非空二叉树的第i层上最大节点数为2(i-1)个节点。
- 在一棵二叉树中,若规定根节点的层数为第一层,则深度为h的二叉树的最大节点数为2h-1个节点。(总结点数)
- 对任何一颗二叉树,若度为0(为叶子节点的)个数为N0,度为2的节点数量为N2,则.有N0=N2+1公式成立。
- 若规定根节点的层数为1,具有n个结点的满二叉树的深度为h=Log2 (n+1);(以2为底的,对数为(n+1))
- 对于具有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否则无右孩子
该性质可用于堆结构中的建堆时,向上调整算法和向下调整算法;相关例子:
6、二叉树的存储结构
二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
🧩、顺序存储结构
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。
🎨、链式存储结构
1、二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。
2、在链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。
3、结构如下:
7、二叉树顺序结构的实现----堆
- 利用堆(一种二叉树)实现。普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。
- 说到堆,就得说一下概念:如果有一个关键码的集合K = { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。简单说就是任意一个父节点都大于子节点的是大堆,反之为小堆,但是左右子节点无大小关系的
此处就整体实现(大/小)堆的创建,代码如下:
1、Heap_Structure.h文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//堆结构体
typedef int HPDataType;
typedef struct Heap
{
HPDataType* _a;
int _size;
int _capacity;
}Heap;
// 堆的构建
void HeapCreate(Heap* hp);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
void HeapPrint(Heap hp);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);
2、Heap_Structure.c文件
#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap_Structure.h"
void HeapCreate(Heap* hp)
{
assert(hp);
hp->_a = NULL;
hp->_capacity = 0;
hp->_size = 0;
}
void HeapPrint(Heap hp)
{
for (int i = 0; i < hp._size; i++)
{
printf("%d ",hp._a[i]);
}
printf("\n");
}
void Swap(HPDataType* x, HPDataType* y)
{
HPDataType tmp = *x;
*x = *y;
*y = tmp;
}
void HeapDestory(Heap* hp)
{
assert(hp);
free(hp->_a);
hp->_a = NULL;
hp->_capacity = 0;
hp->_size = 0;
}
void AdjustUp(HPDataType* a, int child)
{
//向上调整是从最后个叶子结点开始调整,和父节点比较,谁小谁当爹
int parent = (child - 1) / 2;
while (child > 0)
{
//if (a[child] < a[parent])//小堆
if (a[child] > a[parent])//大堆
{
//交换
Swap(&a[child],&a[parent]);
//更新孩子和父节点
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void HeapPush(Heap* hp, HPDataType x)
{
assert(hp);
//检测空间
if (hp->_capacity == hp->_size)
{
int new_cpacity = hp->_capacity == 0 ? 4 : 2 * hp->_capacity;
HPDataType* tmp = (HPDataType*)realloc(hp->_a,sizeof(HPDataType)*new_cpacity);
if (tmp == NULL)
{
perror("malloc fail!");
return;
}
hp->_a = tmp;
hp->_capacity = new_cpacity;
}
hp->_a[hp->_size++] = x;
AdjustUp(hp->_a,hp->_size-1);
}
void AdjustDown(HPDataType* a, int size, int parent)
{
//比较和孩子的大小,谁小谁当爹,但是不知道哪个孩子小些,用假设法
//假设左孩子小
int child = 2 * parent + 1;
while (child < size)
{
//if (child+1<size&&a[child] >a[child + 1])//小堆
if (child + 1 < size && a[child] < a[child + 1])//大堆
{
++child;
}
//判断
//if (a[child] < a[parent])//小堆
if(a[child]>a[parent])
{
Swap(&a[child], &a[parent]);
//更新
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void HeapPop(Heap* hp)
{
//保证堆里有数据
assert(hp&&hp->_size>0);
//交换数据
Swap(&hp->_a[0],&hp->_a[hp->_size-1]);
//直接大小减去1
hp->_size--;
//向下调整
AdjustDown(hp->_a,hp->_size,0);
}
HPDataType HeapTop(Heap* hp)
{
assert(hp&&hp->_size>0);
return hp->_a[0];
}
int HeapSize(Heap* hp)
{
assert(hp);
return hp->_size;
}
int HeapEmpty(Heap* hp)
{
assert(hp);
return hp->_size==0;
}
3、Test.c测试文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap_Structure.h"
int main()
{
int a[] = { 4,2,8,1,5,6,9,7 };
Heap hp;
HeapCreate(&hp);
for (size_t i = 0; i < sizeof(a) / sizeof(int); i++)
{
HeapPush(&hp, a[i]);
}
HeapPrint(hp);
printf("堆顶数据:%d\n",HeapTop(&hp));
//HeapPop(&hp);
HeapPrint(hp);
printf("堆顶数据:%d\n", HeapTop(&hp));
printf("堆中数据量:%d\n",HeapSize(&hp));
HeapDestory(&hp);
return 0;
}
画出图如下:
8、二叉树链式存储实现
- 此棵树手动连接而成:
- 代码实现如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
typedef int BTData;
typedef struct BinaryTreeNode
{
BTData _data;
struct BinarayTreeNode* _left;
struct BinarayTreeNode* _right;
}BTNode;
BTNode* BuyBTNode(BTData x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
perror("malloc fail");
return NULL;
}
node->_data = x;
node->_left = NULL;
node->_right = NULL;
return node;
}
BTNode* test_create_binary_tree()
{
BTNode* node1 = BuyBTNode(1);
BTNode* node2 = BuyBTNode(2);
BTNode* node3 = BuyBTNode(3);
BTNode* node4 = BuyBTNode(4);
BTNode* node5 = BuyBTNode(5);
BTNode* node6 = BuyBTNode(6);
BTNode* node7 = BuyBTNode(7);
(BTNode*)node1->_left = node2;
(BTNode*)node1->_right = node4;
(BTNode*)node2->_left = node3;
(BTNode*)node4->_left = node5;
(BTNode*)node4->_right = node6;
(BTNode*)node3->_left = node7;
//返回二叉树顶端数据节点
return node1;
}
//前序遍历二叉树
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("%s ", "NULL");
return;
}
printf("%d ",root->_data);
PrevOrder((BTNode*)root->_left);
PrevOrder((BTNode*)root->_right);
}
//中序遍历
void InOder(BTNode* root)
{
if (root == NULL)
{
printf("%s ", "NULL");
return;
}
InOder((BTNode*)root->_left);
printf("%d ", root->_data);
InOder((BTNode*)root->_right);
}
//后序遍历
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("%s ","NULL");
return;
}
PostOrder((BTNode*)root->_left);
PostOrder((BTNode*)root->_right);
printf("%d ",root->_data);
}
int BTSize(BTNode* root)
{
/*if (root == NULL)
{
return 0;
}
else
{
return BTSize((BTNode*)root->_left) + BTSize((BTNode*)root->_right) + 1;
}*/
return root==NULL?0:BTSize((BTNode*)root->_left) + BTSize((BTNode*)root->_right) + 1;
}
int BTLeafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
if ((BTNode*)root->_left == NULL && (BTNode*)root->_right == NULL)
{
return 1;
}
return BTLeafSize((BTNode*)root->_left) + BTLeafSize((BTNode*)root->_right);
}
int BTHeight(BTNode* root)
{
if (root == NULL)
{
return 0;
}
return BTHeight((BTNode*)root->_left) > BTHeight((BTNode*)root->_right) ?
BTHeight((BTNode*)root->_left) + 1 : BTHeight((BTNode*)root->_right) + 1;
}
int main()
{
BTNode* root=test_create_binary_tree();
printf("前序遍历:");
PrevOrder(root);
printf("\n");
printf("\n");
printf("中序遍历:");
InOder(root);
printf("\n");
printf("\n");
printf("后序遍历:");
PostOrder(root);
printf("\n");
printf("\n");
printf("二叉树大小:%d\n", BTSize(root));
printf("叶子节点个数:%d\n", BTLeafSize(root));
printf("二叉树高度:%d\n", BTHeight(root));
return 0;
}
下面通过前序遍历创建二叉树,并实现简单的函数:先看代码
1、Binary_Tree_code.c文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include"Queue.h"
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* 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);
int main()
{
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
char array[] = {'A','B','D','#','#','E','#','H','#','#','C','F','#','#','G','#','#','\0'};
//scanf("%s", array);
int index = 0;
//int lenght = sizeof(array) / sizeof(array[0]);
BTNode* root=BinaryTreeCreate(array, &index);
printf("前序遍历创建二叉树:");
BinaryTreePrevOrder(root);
printf("\n中序遍历二叉树打印:");
BinaryTreeInOrder(root);
printf("\n后序遍历二叉树打印:");
BinaryTreePostOrder(root);
printf("\n二叉树节点个数:%d\n",BinaryTreeSize(root));
printf("二叉树的叶子节点个数:%d\n", BinaryTreeLeafSize(root));
int k = 0;
printf("请输入要查看的层数:");
scanf("%d", &k);
printf("二叉树第%d层的节点数为%d\n",k, BinaryTreeLevelKSize(root, k));
printf("查找字符为E的节点:%p\n", BinaryTreeFind(root, 'E'));
printf("二叉树层序遍历打印:");
BinaryTreeLevelOrder(root);
printf("是否为完全二叉树:%d\n", BinaryTreeComplete(root));
BinaryTreeDestory(root);
return 0;
}
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
//创建空间
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
if (root == NULL)
{
perror("malloc fail");
return NULL;
}
if (a[*pi] == '#')
{
(*pi)++;
return NULL;
}
root->_data = a[*pi];
(*pi)++;
root->_left=BinaryTreeCreate(a, pi);
root->_right=BinaryTreeCreate(a, pi);
return root;
}
void BinaryTreeDestory(BTNode* root)
{
if (root == NULL)
{
return;
}
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)
{
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return BinaryTreeLevelKSize(root->_left,k-1)+BinaryTreeLevelKSize(root->_right, k - 1);
}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->_data == x)
{
return root;
}
BTNode* ret1 = BinaryTreeFind(root->_left, x);
if (ret1)
return ret1;
BTNode* ret2 = BinaryTreeFind(root->_right, x);
if (ret2)
return ret2;
return NULL;
}
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("%c ", '#');
return;
}
printf("%c ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
{
printf("%c ",'#');
return;
}
BinaryTreeInOrder(root->_left);
printf("%c ",root->_data);
BinaryTreeInOrder(root->_right);
}
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
printf("%c ",'#');
return;
}
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%c ",root->_data);
}
void BinaryTreeLevelOrder(BTNode* root)
{
Queue queue;
QueueInit(&queue);
if (root!= NULL)
{
QueuePush(&queue,root);
}
while (!QueueEmpty(&queue))
{
BTNode* pcur = QueueFront(&queue);
QueuePop(&queue);
/*if (pcur == NULL)
{
printf("%c",'#');
return;
}*/
printf("%c ", pcur->_data);
if (pcur->_left != NULL)
{
QueuePush(&queue,pcur->_left);
}
if (pcur->_right != NULL)
{
QueuePush(&queue, pcur->_right);
}
}
QueueDestroy(&queue);
}
int BinaryTreeComplete(BTNode* root)
{
Queue queue;
QueueInit(&queue);
if (root != NULL)
{
QueuePush(&queue, root);
}
while (!QueueEmpty(&queue))
{
BTNode* pcur = QueueFront(&queue);
QueuePop(&queue);
if (pcur == NULL)
{
break;
}
QueuePush(&queue, pcur->_left);
QueuePush(&queue, pcur->_right);
}
while (!QueueEmpty(&queue))
{
BTNode* pcur = QueueFront(&queue);
QueuePop(&queue);
if (pcur)
{
QueueDestroy(&queue);
return false;
}
}
QueueDestroy(&queue);
return true;
}
2、由于层序遍历需要用到队列,下面是队列实现:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.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* pq);
void QueueDestroy(Queue* pq);
// 队尾插入
void QueuePush(Queue* pq, QDataType x);
// 队头删除
void QueuePop(Queue* pq);
// 取队头和队尾的数据
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
//
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);
#include "Queue.h"
#define _CRT_SECURE_NO_WARNINGS 1
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* pcur = pq->phead;
while (pcur != NULL)
{
QNode* Next = pcur->next;
free(pcur);
pcur = Next;
}
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
QNode* node = (QNode*)malloc(sizeof(QNode));
if (node == NULL)
{
perror("malloc fail");
return;
}
node->next = NULL;
node->val = x;
//判断尾指针是否为空
if (pq->ptail == NULL)
{
pq->phead = pq->ptail = node;
}
else
{
pq->ptail->next = node;
pq->ptail = node;
}
pq->size++;
}
void QueuePop(Queue* pq)
{
assert(pq);
if (pq->size == 0)
{
printf("队列数据为空,无法删除");
return;
}
队头删除数据
//QNode* Next = pq->phead->next;
//free(pq->phead);
//pq->phead = Next;
//pq->size--;
//但是得分情况①只有一个节点的时候
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = NULL;
pq->ptail = NULL;
}
else
{
QNode* Next = pq->phead->next;
free(pq->phead);
pq->phead = Next;
}
pq->size--;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->phead);
if (pq->size == 0)
{
printf("数据为空!");
}
return pq->phead->val;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->ptail);
return pq->ptail->val;
}
int QueueSize(Queue* pq)
{
assert(pq);
assert(pq->size);
return pq->size;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size==0;
}
结果如下:
9、部分代码递归解析
10、二叉树四种遍历
1、前序遍历:根–左–右
2、中序遍历:左–根–右
3、后序遍历:左–右–根
4、层序遍历:从第一层,一层一层遍历
遍历的一些例子:
递归实现的二叉树到此为止了,非递归于后再描述,谢谢!🚗🚗✔