数据结构---二叉树

二叉树的性质和存储结构

二叉树的性质

1.在二叉树的第i层上至多有2^i-1个结点(i>=1),最少有1个结点

2.深度为K的二叉树至多有 (2^k)-1 个结点 (k>=1),最少有k个结点

  • 将所有层的最大结点数相加

3.对任何一棵二叉树T, 如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1

  • 结点总数n=n1+n2+n0
  • 除了根结点,其余结点都有和双亲的连接(数目用B表示)可得n=B+1
  • 总连接数B=n1+2*n2,可得n=n1+2 *n2+1
  • 综上可得n0=n2+1

满二叉树:深度为 K且含有(2^k)-1个结点的二叉树

  • 除了根结点和叶子结点外所有结点都有左右两个儿子
  • 每一层上的结点数都是最大结点数

完全二叉树:深度为K的, 有n个结点的二叉树, 当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时, 称之为完全二叉树。

这里的对应相当于标号和结点的值都对应

  • 叶子结点只可能在层次最大的两层上出现(上一层出现了叶子,那么根据完全二叉树的定义不会出现下一层)
  • 对任一结点, 若其右分支下的子孙的最大层次为l, 则其左分支下的子孙的最大层次必为 l 或 l+ 1。

(有右子树一定有对应层次的左子树,有左子树不一定有对应的右子树)

4.-image-20220125191900805-结点和深度的关系

5.image-20220125191933078

---双亲和左右孩子编号的关系

二叉树的存储结构

顺序存储
#define MAXTSIZE 100//二叉树的最大结点数
typedef TElemType SqBiTree [MAXTSIZE];//定义类型名为SqBiTree来表示二叉树类型,每一个结点类型为TElemType,然后最大结点数为MAXSIZE
SqBiTree bt;

完全二叉树---从根起按层序存储即可,依次自上而下、自左至右存储结点元素,即将完全二叉树上编号为i 的结点元素存储在如 上定义的一维数组中下标为i-1的分量中

一般二叉树---将其每个结点与完全二叉树上的结点相对照,存储在一维数组的相应分量中,不存在该节点编号还是要编的,只是在对应的数组分量中存0

链式存储
typedef struct BiTNode{
TElemType data; //数据域
struct BiTNode *lchild,*rchild;//左右孩子指针,还有可以有一个指向双亲结点的指针
} BiTNode,*BiTree;

对于完全二叉树,用顺序存储比较方便,但是很多一般的二叉树它并不是所有结点都有左右孩子,这就导致用数组来存储时有效存储空间密度很低,所以对于这种采用链式存储

遍历二叉树

按某条搜索路径巡访树中每个结点,使得每个结点均被访问一次,而且仅被访问一次

遍历的路径相当于将二叉树上所有结点都排成一个线性队列

二叉树的基本单元是根结点、左子树和右子树。因此,若能依次遍历(递归)这三部分,便是遍历了整个二叉树。按照先左结点后右结点,有三种遍历情况

若二叉树不为空

先序:访问根结点--->先序遍历左子树--->先序遍历右子树

中序:中序遍历左子树--->访问根结点--->中序遍历右子树

后序:后序遍历左子树--->后序遍历右子树--->访问根结点

image-20220125192034475

中序遍历算法
Status InOrderTraverse(BiTree T)
{
    if(T){
		InOrderTraverse(T->lchild);
		cout<<T->data;
		InOrderTraverse(T->rchild);
	}
    return OK;
}

三种遍历算法区别就在cout<data;语句的位置,这里左右子树仍是二叉树依旧使用中序遍历,就用到了函数的递归

中序遍历的非递归算法
void InOrderTraverse(Stack* &S, BiTreeNode* &T)//中序遍历二叉树的非递归算法
{
    InitStack(S);//初始化栈
    BiTreeNode* p=T;//指针p指向根结点
    BiTreeNode* q;//申请一个结点q,来存放栈顶弹出的元素
    while(p||!StackEmpty(S))
    {
        if(p)//当p=NULL且栈空时, 循环结束
        {
            
            Push(S,p);//根指针进栈
            p=p->LChild//遍历左子树
        }
        else
        { 
            q=Pop(S);//出栈元素指针保存在q中
            cout<<q->data;//访问根结点
            cout<<"  ";
            p=q->RChild;//遍历右子树
        }
    }
}

若结点数为n,每个结点被访问一次,时间复杂度为O(n),栈的最大容量为树的深度,最坏的情况(一个层次一个结点)空间复杂度也为O(n)

根据遍历顺序确定二叉树

后序序列和中序序列均能唯一地确定一棵二叉树

---通过后序序列可以确定根结点,根据中序序列可以确定左右子树

二叉树的先序序列和中序序列也可唯一地确定一棵二叉树

---通过先序序列可以确定根结点,根据中序序列可以确定左右子树


先序遍历的顺序建立二叉链表
void CreateBiTree(BiTree &T)
{
cin>>ch; //输入结点的值
if(ch== '#') T=NULL;//以#结束
else
{
T=new BiTNode; //生成根结点
T-> data=ch; //把ch赋给根结点的数据域
CreateBiTree (T-> lchild); //递归构建左子树
CreateBiTree (T-> rchild);//递归构建右子树
}
}
复制二叉树
void Copy(BiTree T,BiTree &NewT)//复制二叉树T,将复制后的NewT返回
{
if(T==NULL) //若T为空树,复制空树
{
NewT=NULL; 
return;
}
else//T不为空树
{
NewT=new BiTNode; //复制根结点
NewT-> data=T->data; 
Copy (T-> lchild, NewT-> lchild); //递归复制左子树
Copy (T-> rchild, NewT-> rchild);//递归复制右子树
}
}
计算二叉树的深度
int Depth(BiTree T)
{
if(T==NULL) return 0;
else
{
m=Depth {T->lchild); //递归计算左子树的深度
n=Depth {T->rchild) ; //递归计算右子树的深度
if{m>n) return{m+l);//整个的深度还要加上根节点所占的一层
else return(n+l);
}
}
统计二叉树中结点的个数
int NodeCount(BiTree T)
{
if (T==NULL) return O; //如果是空树,则结点个数为0, 递归结束
else return NodeCount (T->lchild) +Node Count (T->rchild) + 1;//否则返回左右子树的结点之和再加上根节点
}

posted on   眉目作山河  阅读(486)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示