11.树与二叉树

1.树

1.1树的基本概念

空树:节点为0的树

非空树:

在这里插入图片描述

非空树的特性:

  • 有且只有一个根结点
  • 没有后继的结点称为“叶子结点”(或终端结点)
  • 有后继的结点称为“分支结点”(或非终端结点)
  • 除了根结点外,任何一个结点都只有一个前驱
  • 每个结点可以有0个或多个后继

子树根结点的后继结点互不相交,且自身往下又是一棵树,则把这个集合称为根结点的子树。

在这里插入图片描述

在这里插入图片描述

  • 树是一种递归定义的数据结构

  • 两个结点的路径:从前趋结点到后继结点的路线,只能从上往下,不能反着走

  • 路径长度:即路径经过了几条边,长度就为几

  • 结点的层次(深度):从上往下数的层数

  • 结点的高度:从下往上数层数,有几层就有几高

  • 树的高度(深度):一棵树总共有几层

  • 结点的度:即有几个分支,就是度为几

  • 树的度:即各结点的度的最大值

  • 有序树:逻辑上看,树中结点的各子树从左至右是有次序的,不能互换

  • 无序树:逻辑上看,树中结点的各子树从左至右是无次序的,可以互换

  • 森林:森林是任意棵(也可以为0棵)不相交的树的集合

1.2树的性质

结点数 = 总度数 + 1

在这里插入图片描述

  • 度为m的树第i层至多有m^i-1个结点(i>=1)
  • m叉树第i层至多有m^i-1个结点(i>=1)

在这里插入图片描述

2.二叉树

2.1二叉树的基本定义和术语

二叉树是n(n>=0)个结点的有限集合:

  1. 可以为空二叉树,即n = 0。
  2. 或者由一个根结点和两个互不相交的被称为根的左子树右子树组成。左子树和右子树又分别是一颗二叉树。

特点:每个结点至多只有两棵子树,左右子树不能颠倒(二叉树是有序数

在这里插入图片描述

二叉树是递归定义的数据结构

二叉树的五种状态:

在这里插入图片描述

2.2特殊的二叉树

  • 满二叉树。一棵高度为h,且含有(2^h)-1个结点的二叉树

在这里插入图片描述

特点:

  1. 只有最后一层有叶子结点
  2. 不存在度为1的结点
  3. 按层序从1开始编号,结点i的左孩子为2i,右孩子2i+1;结点i的父亲节为[i/2] (如果有的话)
  • 完全二叉树。当且其每个结点都与高度为h的满二叉树中编号的结点一一对应时,称为完全二叉树

在这里插入图片描述

特点:

  1. 只有最后两层可能有叶子结点,叶子结点必须先有左结点才能有右结点

  2. 最多只有一个度为1的结点

  3. 按层序从1开始编号,结点i的左孩子为2i,右孩子2i+1;结点i的父亲节为[i/2] (如果有的话)

  4. 当i<=|n/2|时为分支结点,当i>=|n/2|时为叶子结点

  • 二叉排序树。一棵二叉树或者是空二叉树,或者是具有如下性质的二叉树:
    • 左子树上所有结点的关键字均小于根结点的关键字
    • 右子树上所有结点的关键字均大于根结点的关键字
    • 左子树和右子树又各是一棵二叉排序树

在这里插入图片描述

  • 平衡二叉树。树上任一结点的左子树和右子树得到深度之差不超过1

在这里插入图片描述

平衡二叉树也是一个二叉排序树,其拥有更高的搜索效率。

2.3二叉树的性质

  • 设非空二叉树中度为0,1和2的结点个数分别为n0、n1、n2,则n0 = n2 + 1(叶子结点比二分支结点多一个)

假设树中结点总数为n,则

  1. n = n0 + n1 + n2
  2. n = n1 + 2n2 + 1

在这里插入图片描述

  • 二叉树第i层至多有2^(i-1)个结点(i>=1),

    m叉树第i层至多有m^(i-1)个结点(i>=1)

  • 高度为h的二叉树至多有2h-1个结点(满二叉树),高度为h的m叉树至多有mh-1/m-1个结点

在这里插入图片描述

  • 对于完全二叉树,可以由结点数n推出度为0、1和2的结点个数为n0、n1、n2

  • 完全二叉树最多智能有一个度为1的结点,即

    n1 = 0 或 1,n0 = n2 + 1 , n0 + n2 一定是奇数

  • 若完全二叉树有2k个(偶数)个结点,则必有n1=1, n0=k, n2=k-1

  • 若完全二叉树有2k-1个(奇数)个结点,则必有n1=0, n0=k, n2=k-1

在这里插入图片描述

性质总结:

二叉树:

  • n0 = n2 + n1
  • 第i层至多有2^(i-1)个结点(i>=1)
  • 高度为h的二叉树至多有2^h-1个结点

完全二叉树:

  • 对于完全二叉树,可以由结点数n推出度为0、1和2的结点个数为n0、n1、n2(突破点:完全二叉树最多只会有一个度为1的结点)

3.二叉树的存储结构

3.1顺序存储

  • 在二叉树的顺序存储中,一定要把二叉树的结点编号与完全二叉树对应起来

完全二叉树:

在这里插入图片描述

代码:

//定义结构体
#define MaxSize 100
struct TreeNode {
    ElemType value;	//结点中的数据元素
    bool isEmpty;	//结点是否为空
};
TreeNode t[MaxSize];

定义一个长度为MaxSize的数组t,按照从上至下,从左至右的顺序依次存储完全二叉树中的各个结点

在这里插入图片描述

非完全二叉树时:

在这里插入图片描述

在这里插入图片描述

结论:二叉树的顺序存储结构,只适合存储完全二叉树。

  • i的左孩子为2i
  • i的右孩子为2i+1
  • i的父结点为i/2

3.2链式存储

非完全二叉树时:

在这里插入图片描述

代码:

//二叉树的结点(链式存储)
typedef struct BiTNode{
    ElemType data;	//数据域
    struct BiTNode *lchild,*rchild;	//左右孩子指针
}BiTNode,*BiTree;

在这里插入图片描述

  • n个结点的二叉链表共有 n+1 个空链域

构建链式存储二叉树代码:

struct ElemType{
    int value;
};

//定义结构体BiTNode
typedef struct BiTNode{
    ElemType data;
    struct BiTNode *lchild,*rchild;//定义左右孩子结点指针
    struct BiTNode *parent;	//定义父结点指针(当业务需要频繁寻找结点的父结点时在定义本句)
}BiTNode,*BiTree;

//定义一棵空树
BiTree root = NULL;	//声明一个指向根结点的指针

//插入根结点
root = (BiTree) malloc(sizeof(BiTNode));//用malloc函数申请一个根结点root
root->data = {1};	//往根结点中存入数字1
root->lchild = NULL;//让根结点左右指针指向NULL
root->rchild = NULL;

//插入新结点
BiTNode * p = (BiTree *) malloc(sizeof(BiTNode));//用malloc函数申请一个新结点p
p->data = {2};	//往新结点中存入数字2
p->lchild = NULL;
p->rchild = NULL;
root->lchild = p;	//把根结点的左孩子指针指向结点p

4.二叉树的遍历

遍历:按照某种次序把所有结点都访问一遍

先/中/后序遍历:基于树的递归特性确定的次序规则

先序遍历:根左右

中序遍历:左根右

后序遍历:左右根

在这里插入图片描述

在这里插入图片描述

4.1先序遍历

先序遍历的操作过程如下:

  1. 若二叉树为空,则什么也不做;
  2. 若二叉树非空:
    1. 访问根结点;
    2. 先序遍历左子树;
    3. 先序遍历右子树。
//定义二叉树结构体
typedef struct BiTNode{
    ElemType data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

//先序遍历
void PreOrder(BiTree T){
    if(T!=NULL){	//判断二叉树是否为空
        visit(T);	//访问根结点
        PreOrder(T->lchild);//递归遍历左子树
        PreOrder(T->rchild);//递归遍历右子树
    }
}

4.2中序遍历

中序遍历的操作过程如下:

  1. 若二叉树为空,则什么也不做;
  2. 若二叉树非空:
    1. 先序遍历左子树;
    2. 访问根结点;
    3. 先序遍历右子树。
//定义二叉树结构体
typedef struct BiTNode{
    ElemType data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

//中序遍历
void InOrder(BiTree T){
    if(T!=NULL){	//判断二叉树是否为空
        InOrder(T->lchild);//递归遍历左子树
        visit(T);	//访问根结点
        InOrder(T->rchild);//递归遍历右子树
    }
}

4.3后序遍历

后序遍历的操作过程如下:

  1. 若二叉树为空,则什么也不做;
  2. 若二叉树非空:
    1. 先序遍历左子树;
    2. 先序遍历右子树;
    3. 访问根结点。
//定义二叉树结构体
typedef struct BiTNode{
    ElemType data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

//中序遍历
void PostOrder(BiTree T){
    if(T!=NULL){	//判断二叉树是否为空
        PostOrder(T->lchild);//递归遍历左子树
        PostOrder(T->rchild);//递归遍历右子树
        visit(T);	//访问根结点
    }
}

以上递归算法的空间复杂度为:O(h)

4.4层序遍历

算法思想:

  1. 初始化一个辅助队列
  2. 根结点入队
  3. 若队列非空,则队头结点出队,访问该结点,并将其左,右孩子插入队尾(如果有的话)
  4. 重复步骤3直到队列为空

在这里插入图片描述

在这里插入图片描述

代码实现:

//层序遍历
void LevelOrder(BiTree T){
    LinkQueue Q;	//定义链队列
    InitQueue(Q);	//初始化辅助队列
    BiTree p;
    EnQueue(Q,T);	//将根结点入队
    while(!IsEmpty(Q)){	//队列不空则循环
        DeQueue(Q,p);	//队头结点出队
        visit(p);	//访问出队结点
        if(p->lchild!=NULL)
            EnQueue(Q,p->lchild);	//左孩子入队
        if(p->rchild!=NULL)
            EnQueue(Q,p->rchild);	//右孩子入队
    }
}

5.由遍历序列构造二叉树

  • 若只给出一棵二叉树的 前/中/后/层 序遍历序列中的一种,不能唯一确定一棵二叉树。

  • 只有同时给出以下几种遍历序列的其中两种才可以确定一棵二叉树:

在这里插入图片描述

5.1前序+中序遍历序列

前序遍历:根结点,前序遍历左子树,前序遍历右子树

中序遍历:中序遍历左子树,根结点,中序遍历右子树

在这里插入图片描述

5.2后序+中序遍历序列

后序遍历:前序遍历左子树,前序遍历右子树,根结点

中序遍历:中序遍历左子树,根结点,中序遍历右子树

在这里插入图片描述

5.3层序+中序遍历序列

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值