一、存储结构
1.1顺序存储,在逻辑上连续的元素,在物理存储单元上也连续(只适合存储完全二叉树)
#define MaxSize 100
struct TreeNode{
int value;
bool isEmpty;
};
//实例化
TreeNode t[MaxSize];
1.2链式存储
typedef struct BiTNode{
int data; //数据域
struct BitNode *lchild, *rchild; // 左右孩子指针
}BiTNode, *BiTree;
n个结点的二叉链表有n+1个空链域, 可以用来构造线索二叉树;
构建过程:
三叉链表:在上述二叉链表的基础上,增加父指针指向父节点。
二、二叉树的 先、中、后序遍历和层次遍历
代码使用递归实现:
先序:根左右
void PerOrder(BiTree T){
if(T!=NULL){
visit(T); // 访问根结点
PreOrder(T->lchild);
PreOrder(T->rChild); // 递归遍历左右子树
}
}
中序:左根右
void InrOrder(BiTree T){
if(T!=NULL){
PreOrder(T->lchild);
visit(T);
PreOrder(T->rChild);
}
}
后序:左右根
void PosrOrder(BiTree T){
if(T!=NULL){
PreOrder(T->lchild);
PreOrder(T->rChild);
visit(T);
}
}
层序遍历:
①、初始化辅助队列(仅允许一边进,另一边出)
②、根结点入队
③、若队列非空,队头结点出队,访问其孩子结点并入队(左、右)
重复③至队列为空
//以链式存储的方式实现一个二叉树
typedef strcut BiTNode{
char data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
//链式队列记录结点
typedef struct LinkNode{
BiTNode *data;
struct LinkNode *next;
}LinkNode;
typedef struct{
LinkNode *front, *rear; //队头、队尾
}LinkQueue;
//层序遍历
void LevelOrder(BiTree T){
LinkQueue Q;
InitQueue(Q); 初始化辅助队列
BiTree p; 根节点
EnQueue(Q, p); 根节点入队
while(!IsEmpty(Q)){
DeQueue(Q, p); 队头结点出队
visit(p);
if(p->lchild!=NULL)
EnQueue(Q, p->lchild); 左孩子入队
if(p->rchild!=NULL)
EnQueue(Q, p->rchild); 右孩子入队
}
}
三、由遍历序列构造二叉树
需要以上四种遍历的两种,其中一种必须为中序遍历,才能唯一确定二叉树。
根据不同遍历方式的访问顺序,来确定根结点、左右子树根节点....重复。
四、线索二叉树(线索链表)
让结点的左右孩子指针指向前驱和后继,这样就可以在任意结点(好像也不是任意,当有左右孩子的时候比较麻烦)处找到此结点在前/中/后序遍历排序的前驱和后继。
为了不混淆左右孩子指针指向的到底是此结点的左右孩子,还是此结点在遍历排序中的前驱后继,代码实现的过程中需要是用ltag和rtag来区分,为0:指向孩子;为1:是线索指针
typedef struct ThreadNode{
int data;
struct ThreadNode *lchild, *rchild;
int ltag, rtag;
} ThreadNode, *ThreadTree;
五、利用线索二叉树找指定结点的前驱后继
中序:rtag=1:则没有孩子结点,指向的就是后继结点;
rtag=0:有右孩子结点,则其子树(这个右孩子的子树)的最左下角的左孩子结点为其后继;
ThreadNode *Firstnode(ThreadNode *p){
循环找到最左下角的结点(不一定是叶子结点) (中序)
while(p->ltag == 0)
p = p->lchild;
return p;
}
......