数据结构-二叉树
基本概念
二叉树:一个有穷的结点的集合,这个集合可以为空,若不为空,则它是由根节点和称为其左子树和右子树的两个不相交的二叉树组成。
有几种特殊的二叉树,分别是斜二叉树、完全二叉树和满二叉树。
二叉树的几个重要性质:
一个二叉树第i层的最大结点数为:2^(i-1),i>=1
深度为k的二叉树有最大结点总数为:2^k-1,k>=1
对任何非空二叉树T,若n0表示叶结点的个数,n2是度为2的非叶结点的个数,那么二者满足关系n0=n2+1
抽象数据类型描述
类型名称:二叉树
数据对象集:一个有穷的结点集合,若不为空,则由根结点和其左、右二叉子树组成
操作集:BT是BinTree类型,Item是ElementType类型
- BinTree CreateBinTree():创建一个二叉树
- bool IsEmpty(BinTree BT):判断BT是否为空
- void Traversal(BinTree BT):遍历,按某顺序访问每个结点
常用的遍历方法有: - void PreOrderTraversal(BinTree BT):先序遍历——根、左子树、右子树
- void InOrderTraversal(BinTree BT):中序遍历——左子树、根、右子树
- void PostOrderTraversal(BinTree BT):后序遍历——左子树、右子树、根
- void LevelOrderTraversal(BinTree BT):层次遍历——从上到下、从左到右
顺序存储结构
对于完全二叉树,我们可以按从上至下、从左到右的顺序进行存储
若二叉树根节点的序号为1,则n个结点的完全二叉树的结点父子关系为:
非根结点(序号i>1)的父结点的序号是i/2(向下取整)
结点(序号i)的左孩子结点的序号是2i(2i<=n,否则没有左孩子)
结点(序号i)的右孩子结点的序号是2i+1(2i+1<=n,否则没有右孩子)
对于一般二叉树,我们也可以采取这种结构,但是会造成空间浪费,所以二叉树普遍采用链式存储。
链式存储结构
结构体定义
typedef struct TNode *Position;
typedef Position BinTree; //二叉树类型
struct TNode{
ElementType Data; //结点数据
BinTree Left; //指向左子树
BinTree Right; //指向右子树
};
先序遍历
递归形式
void PreOrderTraversal(BinTree BT){
if(BT){
printf("%d ",BT->Data);
PreOrderTraversal(BT->Left);
PreOrderTraversal(BT->Right);
}
}
非递归形式
一般而言,递归算法转化为非递归形式的基本思路是使用堆栈。
void PreOrderTraversal(BinTree BT){
BinTree T=BT;
Stack S=CreateStack(MaxSize);
while(T||!IsEmpty(S)){
while(T){
printf("%d ",T->Data); //先打印根结点
Push(S,T); //一直向左并沿途结点压入堆栈直到没有左孩子结点
T=T->Left;
}
if(!IsEmpty(S)){
T=Pop(S); //弹出结点
T=T->Right; //转向右子树
}
}
}
中序遍历
递归形式
void InOrderTraversal(BinTree BT){
if(BT){
InOrderTraversal(BT->Left);
printf("%d ",BT->Data);
InOrderTraversal(BT->Right);
}
}
非递归形式
void InOrderTraversal(BinTree BT){
BinTree T=BT;
Stack S=CreateStack(MaxSize);
while(T||!IsEmpty(S)){
while(T){
Push(S,T); //一直向左并将沿途结点压入堆栈
T=T->Left;
}
if(!IsEmpty(S)){
T=Pop(S);
printf("%d ",T->Data);
T=T->Right;
}
}
}
后序遍历
递归形式
void PostOrderTraversal(BinTree BT){
if(BT){
PostOrderTraversal(BT->Left);
PostOrderTraversal(BT->Right);
printf("%d ",BT->Data);
}
}
非递归形式(较为复杂)
层序遍历
二叉树的层序遍历实现需要借助队列
void LevelOrderTraversal(BinTree BT){
Queue Q;
BinTree T;
if(!BT) //空树直接返回
return;
Q=CreateQueue();
AddQ(Q,BT); //根结点入队
while(!IsEmpty(Q)){
T=DeleteQ(Q);
printf("%d ",T->Data);
if(T->Left)
AddQ(Q,T->Left); //左结点入队
if(T->Right)
AddQ(Q,T->Right); //右结点入队
}
}