首页 > 其他分享 >【树】之二叉树(C语言)(含图解)

【树】之二叉树(C语言)(含图解)

时间:2022-11-17 22:05:26浏览次数:41  
标签:结点 NULL C语言 BTNode pq 二叉树 图解 root left


树&二叉树

  • ​​树​​
  • ​​树的概念及结构​​
  • ​​树的概念​​
  • ​​树的要求​​
  • ​​树的表示​​
  • ​​现实应用​​
  • ​​二叉树​​
  • ​​概念​​
  • ​​特殊的二叉树​​
  • ​​注意​​
  • ​​性质​​
  • ​​顺序存储​​
  • ​​链式存储​​
  • ​​构成&遍历​​
  • ​​结构定义​​
  • ​​前序遍历​​
  • ​​中序遍历​​
  • ​​后序遍历​​
  • ​​构建一个简单的树​​
  • ​​函数递归图——前序遍历​​
  • ​​结点个数​​
  • ​​叶子结点的个数​​
  • ​​层序遍历​​
  • ​​全部代码​​

树的概念及结构

树的概念

树是一种非线性的数据结构,它是由n(n >= 0)个有限结点组成的一个具有层次关系的集合,把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的

  • 有一个特殊的结点,称为根结点,根节点没有前驱结点。
  • 除跟根结点外,其余结点被分成M(M>0)个互不相交的集合T1、T2…Tm,其中每一个集合Ti(1<=i<=m)又是一棵结构与树类似的子树。每颗子树的根节点有且只有一个前驱,可以有0个或多个后继。
  • 因此,树是递归定义的。

结点的度:到底有多少个链接的子节点

叶子结点或终端结点:度为0的结点称为叶子结点,

【树】之二叉树(C语言)(含图解)_新星计划

  • 节点的度:一个节点含有的子树的个数称为该节点的度;如上图:A的为6
  • 叶节点或终端节点:度为0的节点称为叶节点;如上图:B、C、H、I…等节点为叶节点非终端节点或分支节点:度不为0的节点;如上图:D、E、F、G…等节点为分支节点
  • 兄弟节点:具有相同父节点的节点互称为兄弟节点;如上图:B、C是兄弟节点
  • 树的度:一棵树中,最大的节点的度称为树的度;如上图:树的度为6
  • 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  • 树的高度或深度:树中节点的最大层次;如上图:树的高度为4(有两种说法-从0开始还是从1开始,空树-1,空树0)
  • 节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
  • 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
  • 森林:由m (m>0)棵互不相交的多颗树的集合称为森林;(数据结构中的学习并查集本质就是一个森林)——(日常很少碰到森林,并查集就是一个森林)

树的要求

  • 子树是不相交的
  • 除了根结点之外,每个结点有且仅有一个父结点
  • 一个N个结点的树有N-1条边

树的表示

相对于线性表,树的结构就复杂很多了。最常用的表示方法——孩子兄弟表示法。

现实应用

文件系统的目录树,

树在实际当中,不太作为存储数据这个角度去用,因为意义不是很大。

主要用的是二叉树

二叉树

现实中的二叉树

这还是个满二叉树

【树】之二叉树(C语言)(含图解)_新星计划_02

概念

与普通的树最大的不同是它最多只有两个子树。

特殊的二叉树

  1. 满二叉树:每一层都是满的。
    假设一棵满二叉树的高度是 h,那么它的总结点个数是:20+21+22+…2(h-1) =N。
    推导公式:2^h-1 = N;h = log2N+1以2位底N的对数+1。

【树】之二叉树(C语言)(含图解)_队列_03

  1. 完全二叉树
    完全二叉树是个效率很高的数据结构,完全二叉树是由满二叉树引出来的。
    假设树的高度是h,前h-1层是满的,最后一层不满,但是最后一层从左往右都是连续的。
    最后一层最少有一个结点。
    结点个数为:2^h-1-X= N,高度近似为:h = log2N+1+X以二为底N的对数+1

【树】之二叉树(C语言)(含图解)_队列_04

注意

在实际中普通二叉树的增删查改没得意义。

有意义的是搜索二叉树

搜索二叉树:任何一棵树,左子树都比跟要小,右子树都比根要大。在搜索树中查找一个数,最多查找高度次。时间复杂度O(N)。

引申:左右两边的结点数量比较均匀。

接着引出 ——平衡树

  • AVL树
  • 红黑树

学习普通二叉树可以为后面学习复杂的有用的平衡树做铺垫。

性质

1.若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点

2.若规定根节点的层数是1,则深度为h的二叉树的最大节点数是2^-1

3.对于任何一棵二叉树,如果度为0其叶结点个数为n0,度为2的分支结点个数为n2,则有n0 = n2 +1(度为2的结点个数总是比度为0的结点个数多1)

4.若规定根节点的层数是1,具有n个结点的满二叉树的深度是h = log2 N +1(以2为底N的对数+1)

顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

链式存储

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,后面到高阶数据结构如红黑树等会用到三叉链。

构成&遍历

【树】之二叉树(C语言)(含图解)_算法_05

任何一个二叉树由三个部分构成

1.根节点——2.左子树——3.右子树

分治算法:分而治之,大问题分成子问题,子问题再分成子问题,直到无法分割

前序遍历:根左右——
(上图:A-B-D-NULL-NULL-E-NULL-NULL-C-NULL-NULL)

中序遍历:左根右——
(NULL-D-NULL-B-NULL-E-NULL-A-NULL-C-NULL)

后序遍历:左右根——
(NULL-NULL-D-NULL-NULL-E-B-NULL-NULL-C-A)

结构定义

typedef char BinaryTreeType;
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
BinaryTreeType data;
}BTNode;

前序遍历

void PrevOrder(BTNode* root)
{
//如果树是空树就直接return
if (root == NULL )
{
printf("NULL ");
return;
}
printf("%c ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}

中序遍历

void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%c ",root->data);
InOrder(root->right);
}

后序遍历

void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%c ", root->data);
}

构建一个简单的树

//构建一个简单的树
BTNode* A = (BTNode*)malloc(sizeof(BTNode));
A->data = 'A';
A->left = NULL;
A->right = NULL;

BTNode* B = (BTNode*)malloc(sizeof(BTNode));
B->data = 'B';
B->left = NULL;
B->right = NULL;

BTNode* C = (BTNode*)malloc(sizeof(BTNode));
C->data = 'C';
C->left = NULL;
C->right = NULL;

BTNode* D = (BTNode*)malloc(sizeof(BTNode));
D->data = 'D';
D->left = NULL;
D->right = NULL;

BTNode* E = (BTNode*)malloc(sizeof(BTNode));
E->data = 'E';
E->left = NULL;
E->right = NULL;

BTNode* F = (BTNode*)malloc(sizeof(BTNode));
F->data = 'F';
F->left = NULL;
F->right = NULL;

A->left = B;
A->right = C;
B->left = D;
B->right = E;

PrevOrder(A);
printf("\n");

【树】之二叉树(C语言)(含图解)_数据结构_06

函数递归图——前序遍历

【树】之二叉树(C语言)(含图解)_新星计划_07

结点个数

方法一:因为我们要对同一个size进行++,所以要设置一个全局变量size进行++就完事了。

int size = 0;
void TreeSize(BTNode* root)
{
if (root == NULL)
{
return;
}
else
{
size++;
}
TreeSize(root->left);
TreeSize(root->right);
}

但是在进行多次调用的时候因为累加,调用前要先将他置成0。

TreeSize(A);
printf("结点个数%d",size);
size = 0;
TreeSize(B);
printf("结点个数%d", size);

但是这种方法不是线程安全的。引出我们的第二种方法。

方法二:传参

int size = 0;
void TreeSize(BTNode* root,int* psize)
{
if (root == NULL)
{
return;
}
else
{
(*psize)++;
}
TreeSize(root->left, psize);
TreeSize(root->right, psize);
}

调用

int Asize = 0;
TreeSize(A,&Asize);
printf("结点个数%d",Asize);
size = 0;
int Bsize = 0;
TreeSize(B, &Bsize);
printf("结点个数%d", Bsize);

方法三

分治

int TreeSize(BTNode* root)
{
return root == NULL ? 0:TreeSize(root->left) + TreeSize(root->right) + 1;
}

调用

printf("%d", TreeSize(A));

图解

【树】之二叉树(C语言)(含图解)_数据结构_08

叶子结点的个数

叶子结点没有子结点,叶子结点就是度为0的结点。

int TreeLeafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
if (root->left == NULL && root->right == NULL)
{
return 1;
}
return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

流程:判断是否为空,为空返回0,不为空看左右孩子是否都为空,为空返回1,如果上面两个条件都不满足,分别计算左右孩子…孩子的孩子…递归返回相应的根结点。

【树】之二叉树(C语言)(含图解)_数据结构_09

层序遍历

前序中序后序遍历其实也叫深度优先遍历。

层序遍历本质上也叫做广度优先遍历,以根为主一层一层往下遍历。

用队列先进先出的性质,

核心思路:上一层带下一层。

【树】之二叉树(C语言)(含图解)_队列_10

void LevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)//不为空进队
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))//队列不为空就继续
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%c ", front->data);
if (front->left)
{
QueuePush(&q, front->left);

if (front->right)
{
QueuePush(&q, front->right);
}
}
}
printf("\n");
QueueDestory(&q);
}

【树】之二叉树(C语言)(含图解)_算法_11

【树】之二叉树(C语言)(含图解)_算法_12

全部代码


上面是队列


#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
/// 队列
//前置声明
struct BinaryTreeNode;
typedef struct BinaryTreeNode* QueueDataType;//存二叉树的结点
typedef struct QueueNode
{
struct QueueNode* next;
QueueDataType data;
}QueueNode;
//单链表除了尾插还要尾删,所以不会加这个
typedef struct Queue
{
QueueNode* tail;
QueueNode* head;
}Queue;
//初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->tail = pq->head = NULL;
}
//销毁
void QueueDestory(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
while (cur)
{
QueueNode* curNext = cur->next;
free(cur);
cur = curNext;
}
pq->head = pq->tail = NULL;
}
//队尾入
void QueuePush(Queue* pq, QueueDataType x)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
printf("malloc is fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;

if (pq->tail == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
}
//队头出
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->head);//队列是不等于空的
if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
//先保存一下下一个结点
QueueNode* nextNode = pq->head->next;
free(pq->head);
pq->head = nextNode;
}
}
//队头数据
QueueDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->head->data;
}
//队尾数据
QueueDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->tail->data;
}
//是否为空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
//返回数据个数
int QueueSize(Queue* pq)
{
assert(pq);
int size = 0;
QueueNode* cur = pq->head;
while (cur)
{
size++;
cur = cur->next;
}
return size;
}

/// //

typedef char BinaryTreeType;
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
BinaryTreeType data;
}BTNode;
//二叉树不学习增删查改,因为没得意义
//前序遍历
void PrevOrder(BTNode* root)
{
//如果树是空树就直接return
if (root == NULL )
{
printf("NULL ");
return;
}
printf("%c ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
//中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%c ",root->data);
InOrder(root->right);
}
//后序遍历
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%c ", root->data);
}
//结点的个数
/*因为我们要对同一个size进行++,
所以要设置一个全局变量size进行++就完事了,
但是在进行多次调用的时候因为累加,调用前要先将他置成0*/
//int size = 0;
//void TreeSize(BTNode* root)
//{
// if (root == NULL)
// {
// return;
// }
// else
// {
// size++;
// }
// TreeSize(root->left);
// TreeSize(root->right);
//}
//传参
//int size = 0;
//void TreeSize(BTNode* root,int* psize)
//{
// if (root == NULL)
// {
// return;
// }
// else
// {
// (*psize)++;
// }
// TreeSize(root->left, psize);
// TreeSize(root->right, psize);
//}
int TreeSize(BTNode* root)
{
return root == NULL ? 0:TreeSize(root->left) + TreeSize(root->right) + 1;
}
//叶子结点的个数
int TreeLeafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
if (root->left == NULL && root->right == NULL)
{
return 1;
}
return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

//层序遍历
void LevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)//不为空进队
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))//队列不为空就继续
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%c ", front->data);
if (front->left)
{
QueuePush(&q, front->left);

if (front->right)
{
QueuePush(&q, front->right);
}
}
}
printf("\n");
QueueDestory(&q);
}

int main(void)
{
//构建一个简单的树
BTNode* A = (BTNode*)malloc(sizeof(BTNode));
A->data = 'A';
A->left = NULL;
A->right = NULL;

BTNode* B = (BTNode*)malloc(sizeof(BTNode));
B->data = 'B';
B->left = NULL;
B->right = NULL;

BTNode* C = (BTNode*)malloc(sizeof(BTNode));
C->data = 'C';
C->left = NULL;
C->right = NULL;

BTNode* D = (BTNode*)malloc(sizeof(BTNode));
D->data = 'D';
D->left = NULL;
D->right = NULL;

BTNode* E = (BTNode*)malloc(sizeof(BTNode));
E->data = 'E';
E->left = NULL;
E->right = NULL;

BTNode* F = (BTNode*)malloc(sizeof(BTNode));
F->data = 'F';
F->left = NULL;
F->right = NULL;

A->left = B;
A->right = C;
B->left = D;
B->right = E;

LevelOrder(A);


printf("\n");
return 0;
}


标签:结点,NULL,C语言,BTNode,pq,二叉树,图解,root,left
From: https://blog.51cto.com/u_15333750/5866192

相关文章

  • LeetCode刷题(9)【树】前序&深度&平衡(C语言)
    ​二叉树的前序遍历144.二叉树的前序遍历-力扣(LeetCode)(leetcode-cn.com)本题中,对于C++或者Java等语言,返回的是它们的数据结构库里面的数据结构,而C语言没有,这也就是如果......
  • NowCoder刷题(1)【树】二叉树的遍历(含图解)
    二叉树的遍历(IO型)二叉树遍历_牛客题霸_牛客网(nowcoder.com)题目描述如图所示的这棵树前序输出结果为A-B-D-#-#-E-#-#-C-#-#还原过程示例1示例2——前序遍历还原代码实现......
  • C语言文件操作
    相关视频——C语言精华——C语言文件操作,文件打开、关闭、读取、定位如何操作?为你逐一讲解文件操作标准库函数_哔哩哔哩(゜-゜)つロ干杯~-bilibili我的小站——半生瓜のbl......
  • C语言实现学生成绩管理系统
    相关视频——https://www.bilibili.com/video/BV13z4y117qC?p=8我的小站——半生瓜のblog​代码​实现#define_CRT_SECURE_NO_WARNINGS1#include<stdio.h>#include<conio.......
  • LeetCode刷题(1)【链表】【反转链表】(C语言)
    我的小站——半生瓜のblog(doraemon2.xyz)题目链接——206.反转链表-力扣(LeetCode)(leetcode-cn.com)反转链表思路一:反转指针。本质上就是调转指针的方向。首先我们......
  • 【链表】双向循环带头链表-增-删-查(C语言)
    我的小站——半生瓜のblog——同步更新单链表存在的缺陷:不能从后往前走,找不到他的前驱,指定位置删除增加尾删都要找前一个,时间复杂度都是O(n)针对上面的这些缺陷的解决......
  • 【链表】单链表-增-删-查(C语言)
    我的小站——半生瓜のblog单链表​​结构体定义​​​​打印​​​​创建结点​​​​尾插​​​​头插​​​​尾删​​​​头删​​​​查找​​​​在指定位置前插入某个......
  • 【线性表】之顺序表(C语言)
    【线性表】之顺序表​​线性表​​​​顺序表​​​​结构定义​​​​初始化​​​​销毁​​​​打印​​​​扩展空间​​​​尾插​​​​头插​​​​尾删​​​​头删......
  • LeetCode刷题(4)【移除元素&合并两个有序数组】(C语言)(含图解)
    移除元素典型双指针玩法。27.移除元素-力扣(LeetCode)(leetcode-cn.com)我们都会想到这样的解法:从前面依次往后推,是val就将该数据后面的元素依次覆盖上来,但是这样的时间复......
  • LeetCode刷题(5)【链表】【环形链表II】(C语言)
    环形链表I​​LeetCode刷题(3)【链表】【环形链表】&扩展_半生瓜のblog-CSDN博客​​环形链表II142.环形链表II-力扣(LeetCode)(leetcode-cn.com)这个题写起来不难,但是证......