二叉树的特点
- 每个节点最多有两颗子树
- 左右子树有顺序
特殊的树
- 斜树
- 满二叉树
- 完全二叉树
二叉树的顺序存储结构
二叉树的顺序存储结构就是用一组地址连续的存储单元依次自上而下、自左向右存储完全二叉树上的结点元素对于一般的二叉树,我们先转换为完全二叉树进行存储,用空值来代替。
二叉树的链式存储结构
叉树的链式存储结构就是
二叉树每个结点最多有两个孩子,所以设计二叉树的结点结构时,考虑两个指针指向该结点的两个孩子这种结构又被称为二叉链表可以再加一个指针域来指向该结点的双亲结点,那这是三个指针的链表就叫三叉链表。
typedef struct BiTNode{
ElemType data; //数据域
struct BiTNode *lchild,*rchild; //指向该结点的左右孩子的指针
}BiTNode,*BiTree; // 二叉树结点结构
二叉树的遍历
- 先序遍历
- 递归实现
如果二叉树为空,则什么也不做。
否则:
访问根结点
先序遍历左子树
先序遍历右子树void PreOrder(BiTree T) { if(T!=NUll) { printf("%c",T->data); //访问根结点 PreOrder(T->lchild); //递归遍历左子树 PreOrder(T->rchild); //递归遍历右子树 } else { return; } }
- 非递归实现
void PreOrder(BiTree b) { InitStack(S); //初始化一个栈 BiTree p = b; //工作指针p while(p || !IsEmpty(S)) /* **如果当前指针指向的二叉树中一个结点不为NULL **或者 用来存放遍历的二叉树结点的栈不为空 */ { printf("%c",p->data); //先序遍历结点,输入根根结点 Push(S,p); //进栈保存 p = p->lchild; //指向下一个左孩子 } //一直把所有的向左遍历的结点进栈后 //开始出栈,然后把右面的进栈 if(!IsEmpty(S)) // { p = Pop(s) p = p->rchild; } }
- 递归实现
- 中序遍历
- 递归实现
如果二叉树为空,则什么也不做。
否则:
中序遍历左子树
访问根结点
中序遍历右子树void InOrder(BiTree T) { if(T!=NULL) { InOrder(T->lchild); //递归遍历左子树 printf("%c",T->data); //访问根结点 InOrder(T->rchild); //递归遍历右子树 } else { return; } }
- 非递归实现
void Inorder(BiTree b) { InitStack(S); //初始化一个栈 BITree p = b; //工作指针p while(p || !IsEmpty(S)) { while(p) { Push(S,p); //把结点进栈 p = p->lchild; } p = Pop(S); printf("%c",p->data); p = p->rchild; // 遍历右孩子 } }
- 递归实现
- 后序遍历
- 递归实现
如果二叉树为空,则什么也不做。
否则:
后序遍历左子树
后序遍历右子树
访问根结点void PostOrder(BiTree T) { if(T!=NULL) { PostOrder(T->lchild); //递归遍历左子树 PostOrder(T->rchild); //递归遍历右子树 printf("%c",T->data); //访问根结点 } else { return; } }
- 非递归实现
void PostOrder(BiTree b) { InitStack(S); BiTree p = b,r = NULL; //工作指针p,辅助指针r while(p || !IsEmpty) { // 1.从根结点到最左下角的左子树都要入栈 if(p) { Push(S,p); p = p->lchild; } //2.返回栈顶的两种情况 else { GetTop(S,p); //取栈顶元素,不是出栈 //右子树还未访问,而且右子树不空,第一次栈顶 if(p->rchild && p->rchild!=r)) { p = p->rchild; //把右子树遍历 } //右子树已经访问过了,或者未空,接下来就是出栈操作 else { Pop(S,p); //如果访问过 printf("%c",p->data); r = p; //指向访问过的右子树根结点 p = NULL; // 使p为空从而继续访问栈顶 } } } }
- 递归实现
- 层次遍历
若树为空,则什么都不做。直接返回
否则从树的第一层开始访问,从上而下逐层遍历
在同一层中,按从左到右的顺序对结点逐个访问。
访问完一个结点之后,把它的孩子存起来,而且先访问的结点
它的孩子也是先访问。void LevelOrder(BiTree n) { InitQueue(Q); BiTree p; EnQueue(Q,b); // 根结点入队 while(!IsEmpty(Q)) //队列不空循环 { DeQueue(Q,p); //队头元素出队 printf("%c",p->data); if(p->lchild!=NULL) { EnQueue(Q,p->lchild); //把左子树进队列 } if(p->rchild != NULL) { EnQueue(Q,p->rchild); //把右子树进队列 } } }
线索二叉树
指向前驱和后继的指针称为线索,加上线索的二叉链表就称为线索链表,相应的二叉树就称为线索二叉树。
对二叉树以某种次序遍历使其变成线索二叉树的过程称为线索化。
通过在二叉链表结点的结构基础上增加两个标志位 ltag 和 rtag 来进行表标识结点的左右指针是指向孩子结点还是指向前前驱后继结点。
- ltag==0 ——> lchild指向该结点左孩子
- ltag==1 ——> lchild指向该结点前驱结点
- rtag==0 ——> rchild指向该结点右孩子
- rtag==1 ——> rchild指向该结点后继结点是
修改下二叉树的结点的结构
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadNode,*ThreadTree; //线索链表
中序遍历对二叉树线索的递归算法
//