数据结构-二叉树
在学习二叉树之前我们先来了解一下树吧
简单了解树
树的概念
树是一种数据结构,它是由n(n≥0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的
树的特点
形状图:
(1)节点的度:一个节点含有的子树的个数称为该节点的度;如上图:A的为6
(2)叶节点或终端节点:度为0的节点称为叶节点;如上图:B、C、H、I…等节点为叶节点非终端节点或分支节点:度不为0的节点;如上图:D、E、F、G…等节点为分支节点
(3)双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;如上图:A是B
(4)孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;如上图:B是A的孩子节
(5)兄弟节点:具有相同父节点的节点互称为兄弟节点(亲兄弟);如上图:B、C是兄弟节点亲兄弟
(6)树的度:一棵树中,最大的节点的度称为树的度;如上图:树的度为6
(7)节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;树的高度或深度:树中节点的最大层次;如上图:树的高度为4
(8)节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
(9)森林:由m(m>0)棵互不相交的多颗树的集合称为森林;(数据结构中的学习并查集本质就是一日常很少碰到森林,并查集就是一个森林
区分树与非树
二叉树
二叉树的概念
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
二叉树的特点
1.每个结点最多有两棵子树即二叉树不存在度大于2的结点。
2.二叉树的子树有左右之分,其子树的次序不能颠倒。
结构图
特殊的二叉树
1.满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k)-1,则它就是满二叉树。
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=1og√n+1
练习1
1.某二叉树共有 399 个结点,其中有199个度为2的结点,则该二叉树中的叶子结点数为()
A 不存在这样的二叉树
B200
C 198
D 199
解析:
根据上面的性质:度为2的分支结点个数为n2则有n0=n2+1,就可以得出叶节点个数为 :199+1=200,所以这题选择A
2.在具有 2n 个结点的完全二叉树,叶子结点个数为()
A n
B n+1
c n-1
Dn/2
解析:
3.一棵完全二叉树的节点数位为531个,那么这棵树的高度为()
A 11
B 10
C 8
D 12
解析:
实现遍历
1.思想:
按照分治算法的思想,将大问题转化为各个子问题,所以这里我们要用到递归
2.快速构建二叉树
树的图:
struct erchashu {
shuju data;
ST* n;//左
ST* m;//右
};
int main() {
//以下构建5个结点
ST* A = (ST*)malloc(sizeof(ST));
A->data = 'A';
A->n = NULL;
A->m = NULL;
ST* B = (ST*)malloc(sizeof(ST));
B->data = 'B';
B->n = NULL;
B->m = NULL;
ST* C = (ST*)malloc(sizeof(ST));
C->data = 'C';
C->n = NULL;
C->m = NULL;
ST* D= (ST*)malloc(sizeof(ST));
D->data = 'D';
D->n = NULL;
D->m = NULL;
ST* E = (ST*)malloc(sizeof(ST));
E->data = 'E';
E->n = NULL;
E->m = NULL;
//按照树的形状构建
A->n = B;
A->m = C;
B->n = D;
B->m = E;
3.前序
前序是先搜索根,再到左子树,最后右子树
如:
代码实现:
void qian(ST* q) {//前序
//限制条件,当为空时打印NULL并且返回空
if (q == NULL) {
printf("NULL ");
return;
}
else {
printf("%c ", q->data);//打印根的数据
qian(q->n);// 左 迭代后进行递归
qian(q->m);//右 迭代后进行递归
}
}
递归过程图:
运行结果:
4.中序
中序是先左子树,再根,最后右子树
如:
代码实现:
void zho(ST* q) {//中序
//限制条件,当为空时打印NULL并且返回空
if (q == NULL) {
printf("NULL ");
return;
}
else {
zho(q->n);//先左子树,迭代递归
printf("%c ", q->data);
zho(q->m);//后右子树,迭代递归
}
}
运行结果:
5.后序
后序是先左子树,再右子树,最后根
如:
代码实现:
void ho(ST* q) {//后序
if (q == NULL) {
printf("NULL ");
return;
}
else {
ho(q->n);//先左
ho(q->m);//再右
printf("%c ", q->data);
}
}
运行结果:
6.节点个数
代码实现:
int jiedian2(ST* q) {
if (q == NULL)
return 0;//空就返回0
//左右子树和根(这里的1代表的就是根(一个根就是一个节点))累加的结果
return jiedian2(q->n) + jiedian2(q->m) + 1;
}
递归展开图:
运行结果:
7.叶子个数
求叶的个数和求结点的方法差不多,就是返回条件换成了左右为空才返回1
//叶点
int yezi(ST* q) {
if (q->n == NULL && q->m == NULL)//左右都为空就返回1
return 1;
return yezi(q->n) + yezi(q->m);//先从左再到右,最后返回两边叶的和
}
运行结果:
9.以上代码总览
struct erchashu {
shuju data;
ST* n;//左
ST* m;//右
};
void qian(ST* q) {//前序
if (q == NULL) {
printf("NULL ");
return;
}
else {
printf("%c ", q->data);
qian(q->n);
qian(q->m);
}
}
void zho(ST* q) {//中序
if (q == NULL) {
printf("NULL ");
return;
}
else {
zho(q->n);
printf("%c ", q->data);
zho(q->m);
}
}
void ho(ST* q) {//后序
if (q == NULL) {
printf("NULL ");
return;
}
else {
ho(q->n);
ho(q->m);
printf("%c ", q->data);
}
}
//结点-遍历
void jiedian1(ST* q, int* n) {
if (q == NULL)
return;
else {
(*n)++;
jiedian1(q->n,n);
jiedian1(q->m, n);
}
}
//结点-递归
int jiedian2(ST* q) {
if (q == NULL)
return 0;
return jiedian2(q->n) + jiedian2(q->m) + 1;
}
//叶点
int yezi(ST* q) {
if (q->n == NULL && q->m == NULL)
return 1;
return yezi(q->n) + yezi(q->m);
}
int main() {
ST* A = (ST*)malloc(sizeof(ST));
A->data = 'A';
A->n = NULL;
A->m = NULL;
ST* B = (ST*)malloc(sizeof(ST));
B->data = 'B';
B->n = NULL;
B->m = NULL;
ST* C = (ST*)malloc(sizeof(ST));
C->data = 'C';
C->n = NULL;
C->m = NULL;
ST* D= (ST*)malloc(sizeof(ST));
D->data = 'D';
D->n = NULL;
D->m = NULL;
ST* E = (ST*)malloc(sizeof(ST));
E->data = 'E';
E->n = NULL;
E->m = NULL;
A->n = B;
A->m = C;
B->n = D;
B->m = E;
qian(A);
printf("\n");
zho(A);
printf("\n");
ho(A);
printf("\n");
int n = 0;
jiedian1(A, &n);
printf("%d\n", n);
printf("%d\n", jiedian2(B));
printf("%d\n", yezi(A));
return 0;
}
10.广度优先遍历
我们上面的前序和中序后序也叫深度优先遍历
广度优先遍历:
从上到下,一层一层遍历
如:
那么怎么实现捏?
我们这里是通过队列去实现的
队列的特点是先进的先出,那么我们可以让树的根先进队列,再保留根的左子树和右子树,然后让根出列,最后用保留的左右子树作为下一次的根(NULL不用进队),一直遍历到最后队列为空就结束。
执行过程图:
代码实现:
队列:
Queue.h
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
typedef struct erchashu ST;
typedef char shuju;
typedef ST* QDataType;
struct erchashu {
shuju data;
ST* n;//左
ST* m;//右
};
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
}Queue;
void QueueInit(Queue* pq);
void QueueDestory(Queue* pq);
// 队尾入
void QueuePush(Queue* pq, QDataType x);
// 队头出
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);//头出
int QueueSize(Queue* pq);//个数
bool QueueEmpty(Queue* pq);//是否为空
Queue.c
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"
void QueueInit(Queue* pq) {//初始化
assert(pq);
pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x) {//队尾入
assert(pq);
QNode* p = (QNode*)malloc(sizeof(QNode));
if (p == NULL) {
printf("malloc fail\n");
exit(-1);
}
p->data = x;
p->next = NULL;
if (pq->tail == NULL)
pq->head = pq->tail = p;
else {
pq->tail->next = p;
pq->tail = p;
}
}
void QueuePop(Queue* pq) {
assert(pq);
//一个
if (pq->head->next == NULL) {
free(pq->head);
pq->head = pq->tail = NULL;
}
//多个
else {
Queue* p = pq->head->next;
free(pq->head);
pq->head = NULL;
pq->head = p;
}
}
QDataType QueueFront(Queue* pq) {
assert(pq);
assert(pq->head);
return pq->head->data;
}
int QueueSize(Queue* pq) {
assert(pq);
assert(pq->tail);
int n = 0;
QNode* p = pq->head;
while (p)
{
n++;
p = p->next;
}
return n;
}
bool QueueEmpty(Queue* pq) {
assert(pq);
return pq->head==NULL;
}
void QueueDestory(Queue* pq) {
assert(pq);
while (pq->head) {
QNode* p = pq->head->next;
free(pq->head);
pq->head = NULL;
pq->head = p;
}
pq->head = pq->tail = NULL;
}
二叉树:
void guangdu(ST* q) {
Queue p;
QueueInit(&p);
if (q)//不为空进队
QueuePush(&p, q);
while (!QueueEmpty(&p)) {//判断是否为空
ST* te = QueueFront(&p);//用变量保留根
QueuePop(&p);//出列
printf("%c ", te->data);
if(te->n)//不为空就入队
QueuePush(&p, te->n);
if(te->m)
QueuePush(&p, te->m);
}
QueueDestory(&p);//释放
}
int main() {
ST* A = (ST*)malloc(sizeof(ST));
A->data = 'A';
A->n = NULL;
A->m = NULL;
ST* B = (ST*)malloc(sizeof(ST));
B->data = 'B';
B->n = NULL;
B->m = NULL;
ST* C = (ST*)malloc(sizeof(ST));
C->data = 'C';
C->n = NULL;
C->m = NULL;
ST* D= (ST*)malloc(sizeof(ST));
D->data = 'D';
D->n = NULL;
D->m = NULL;
ST* E = (ST*)malloc(sizeof(ST));
E->data = 'E';
E->n = NULL;
E->m = NULL;
A->n = B;
A->m = C;
B->n = D;
B->m = E;
guangdu(A);
return 0;
}
运行结果:
练习2
1.二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK;中序遍历:HFIEJKG.则二叉树根结点为()
A .E
B. F
C.G
D.H
解析:
2.设一课二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为_____。
A adbce
B decab
C debac
D abcde
解析:
总结:
先+中序能还原树
中+后序也能还原树
练习3
二叉树最大深度
分析:
1.我们先算左和右子树的深度,再左右对比返回大的一方再加1
2.我们可以通过递归计算每次左右子树的大小
代码实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
int maxDepth(struct TreeNode* root) {
if(root==NULL)//限制条件
return 0;
int t=maxDepth(root->left);//左子树长度
int f=maxDepth(root->right);//右子树长度
return t>f?t+1:f+1;//对比,返回大的+1
}
以上就是我的分享了,如果有什么错误,欢迎在评论区留言。
最后,谢谢大家的观看!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!