遍历二叉树及其应用
遍历二叉树及其应用
(非递归算法不考!!!)
二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树中的所有结点,使得每个结点被访问一次且仅被访问一次。
二叉树的遍历算法:
1. 前序遍历
若二叉树为空树,则空操作;否则,
(1)访问根结点;
(2)先序遍历左子树;
(3)先序遍历右子树。
遍历的顺序为:ABDGHCEIF
递归实现:
void PreOrderTraverse(BiTree T)
{
if(T==NULL)
return;
printf("%c",T->data);
PreOrderTraverse(T->lchild); //再先序遍历左子树
PreOrderTraverse(T->rchild); //最后先序遍历右子树
}
非递归实现:
-
建立栈,并初始化。
-
若遍历结点存在或者栈不为空
(1) 若结点存在:访问结点,结点入栈,指向其左孩子。
(2)否则:出栈,指向其右孩子。
- 否则,遍历结束。
void PreOrderTraverse(BiTree root)
{
SqStack S;
InitStack(S);
BiTree *p;
p = root;
while(p||!StatckEmpty(S)){
if(p)
{
printf("%c",p->data); //访问根结点
Push(S,p); //保存根结点
p=p->lchild; //遍历左子树
}
else
{
Pop(S,p); //弹出根结点
p=p->rchild; //遍历右子树
}
}
}
2. 中序遍历
若二叉树为空树,则空操作;否则,
(1)中序遍历左子树;
(2)访问根结点;
(3)中序遍历右子树。
遍历顺序为:GDHBAEICF
递归实现:
void InOrderTraverse(BiTree T)
{
if(T==NULL)
return;
InOrderTraverse(T->lchild);
printf("%c",T->data);
InOrderTraverse(T->rchild);
}
非递归实现:
-
建立栈,并初始化。
-
若遍历结点存在或者栈不为空
(1) 若结点存在:结点入栈,指向其左孩子。
(2)否则:出栈,访问结点,指向其右孩子。
- 否则,遍历结束。
void InOrderTraverse(BiTree root)
{
SqStack S;
InitStack(S);
BiTree *p;
p = root;
while(p||!StatckEmpty(S)){
if(p)
{
Push(S,p); //保存根结点
p=p->lchild; //遍历左子树
}
else
{
Pop(S,p); //弹出根结点
printf("%c",p->data); //访问根结点
p=p->rchild; //遍历右子树
}
}
}
3. 后序遍历
若二叉树为空树,则空操作;否则,
(1)后序遍历左子树;
(2)后序遍历右子树;
(3)访问根结点。
遍历顺序为:GHDBIEFCA
递归实现:
void PostOrderTraverse(BiTree T)
{
if(T==NULL)
return;
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
printf("%c",T->data);
}
非递归实现:
弹出条件
1. 判断刚访问过的结点q是不是当前栈顶结点p的右孩子。
2. p的右孩子为空。
void PostOrderTraverse(BiTree root)
{
InitStack(S);
BiTree *p=T,*q; // r标记最近访问过的结点
p = root;
q = NULL;
while(p||!StatckEmpty(S)){
if(p){
Push(S,p); // 一直向左走,左孩子入栈
p=p->lchild;
}
else{
GetTop(S,p);
if(p->rchild && p->rchild!=q){ // 若右孩子存在且未被访问
p=p->rchild; // 就让右孩子
push(S,p) // 入栈
p=p->lchild; // 让右孩子向左
//上面三句意思就是让右孩子的左孩子一直入栈,一直向左走
}
else{
pop(s,p); // 右孩子为空或未被访问过,就出栈
visit(p->data);
q=p; // q标记最近访问结点
p=NULL;
}
}
}
}
4. 层序遍历
若二叉树为空树,则空操作;否则,从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对节点逐个访问。
遍历顺序为:ABCDEFGHI
队列实现:
先根结点入队,然后:
1. 从队列中取出一个元素;
2. 访问该元素所指结点;
3. 若该元素所指结点的左右孩子结点非空,则将其左右孩子顺序入队。
void LevelorderTraverse ( BiTree root) {
Queue Q;
BiTree T;
if (!root)
return; /* 若是空树则直接返回 */
InitQueue(Q); /* 创建空队列Q */
EnQueue(Q,root);
while (!IsEmpty(Q))
{
DeQueue(Q,T);
printf("%d ", T->Data); /* 访问取出队列的结点 */
if ( T->lchild )
EnQueue(Q, T->lchild);
if ( T->rchild )
EnQueue(Q, T->rchild);
}
}
}
二叉树的恢复:(必考)
1. 由先序和中序序列恢复二叉树
二叉树的先序序列:根—>左子树—>右子树
二叉树的中序序列:左子树—>根—>右子树
(1)由先序序列的第一个结点确定根结点D。
(2)通过根结点D分割中序序列:D之前是左子树的中序序列,D之后是右子树的中序序列,同时获得左右子树的结点个数。
(3)根据左子树的结点个数,分割先序序列:第一结点根D,之后是左子树的先序序列,最后是右子树的先序序列。
2. 由中序和后序序列恢复二叉树
二叉树的中序序列:左子树—>根—>右子树
二叉树的后序序列:左子树—>右子树—>根
同理:
二叉树遍历的应用:
1. 统计叶子结点的数目:
解法1:
int LeafCount = 0;
void LeafNum(BiTree root){
if(root!=NULL){
if(root->lchild==NULL&&root->rchild==NULL){
LeafCount++;
}
LeafNum(root->lchild);
LeafNum(root->rchild);
}
}
解法2:
int Leaf(BiTree root){
int LeafCount;
if(root==NULL)
LeafCount=0;
else if(root->lchild==NULL&&root->rchild==NULL){
LeafCount=1;
}
else
LeafCount=leaf(root->lchild)+leaf(root->rchild);
return 1;
}
拓展:
统计二叉树中度为1的结点个数:
(root->lchild==NULL&&root->rchild!=NULL)||(root->lchild!=NULL&&root->rchild==NULL)
统计二叉树中度为2的结点个数:
root->lchild!=NULL&&root->rchild!=NULL
统计二叉树中结点值为x的结点个数:
root->data == x
2. 计算二叉树的高度:
二叉树的高度:二叉树中所有结点层次的最大值
解法1:
int depth = 0;
void PreTreeDepth(BiTree root,int h){
if(root!=NULL){
if(h>depth)
depth = h;
PreTreeDepth(root->lchild,h+1);
PreTreeDepth(root->rchild,h+1);
}
}
解法2:
int PostTreeDepth(BiTree root){
int hl,hr,max;
if(root!=NULL){
hl=PostTreeDepth(root->lchild);
hr=PostTreeDepth(root->lchild);
max=hl>hr?hl:hr;
return max+1;
}
else
return 0;
}
2. 计算二叉树的最大宽度:(利用层序遍历法)
int WidthCount ( BiTree root) {
Queue Q;
BiTree T;
if (!root)
return; /* 若是空树则直接返回 */
InitQueue(Q); /* 创建空队列Q */
int width = 0;
int num = 1;
int max = 0;
EnQueue(Q,root);
while (!IsEmpty(Q))
{
DeQueue(Q,T);
printf("%d ", T->Data); /* 访问取出队列的结点 */
if ( T->lchild )
EnQueue(Q, T->lchild); width++;
if ( T->rchild )
EnQueue(Q, T->rchild); width++;
if(--num == 0){
num = witdh;
if(max < width){
max = width;
}
width = 0;
}
}
return max;
}
二叉树的繁茂度:
int Lushdeg(BiTree root){
return PostTreeDepth(root)*WidthCount(root) ; //繁茂度=高度*最大宽度
}
3. 交换左右子树
void reChange(BiTree root){
if(root == NULL) return;
if(root){
Bitree temp = root->lchild;
root->lchild = root->rchild;
root->rchild = temp;
reChange(root->lchild);
reChange(root->rchild);
}
}
4. 判断二叉树是否为平衡二叉树
bool isAVL (BiTree root){
if(root){
int l = PreTreeDepth(root->lchild);
int r = PreTreeDepth(root->rchild);
if(abs(l-r)!=1) return false;
}
return true;
}
5. 根结点到叶子结点的路径
void RouteToRoot(BiTree root) {
if (root) {
SqStack stack;
InitStack(stack)
push(stack, root->data); //只要root不为空,就将root的值入栈
if (!root->left_child&&!root->right_child)
printStack(stack);
else {
RouteToRoot(root->lchild, stack);
RouteToRoot(root->rchild, stack);
}
pop(stack);
}
}
6. 判断二叉树是否为严格二叉树
bool isStrict (BiTree root){
if(!root)
return true;
if(root){
if((!root->lchild&&root->rchild)||(root->lchild&&!root->rchild))
return false;
}
return isStrict (root->lchild) && isStrict (root->rchild);
}