二叉树的遍历、求深与还原

这些操作是由二叉树的定义推得,对于任意二叉树均适用。

遍历二叉树

二叉树的遍历算法分为深度优先遍历和广度优先遍历(层次遍历),深度优先遍历包括先序遍历,中序遍历和后序遍历。

先序遍历

先序遍历二叉树要求先访问根节点再访问左右子节点,由递归可以容易地实现先序遍历。

int preorder(node *root) {
    if (root != NULL) { //节点不空时执行递归,节点为空时结束递归
        printf("%c ",root->ch);//访问根节点
        num++; //每次访问均使计数器自增
        preorder(root->left);
        preorder(root->right);
    }
     return num;//返回节点数目
}

分析一下递归的过程: 首先访问根节点然后递归遍历左子树,反复执行这一过程直至节点为空结束递归。 此时,递归调用中递归访问左子树的语句执行完成,递归遍历其右子树。 将其右子树作为根节点执行递归执行上述过程。

模拟调用栈的行为,得到非递归遍历算法:

初始化,根节点入栈
当前节点置为根节点
当前节点不空或栈不空时执行循环 {
    若当前节点不空 {
        访问当前节点
        当前节点入栈
        当前节点更新为其左节点
    }
    否则 {
        弹栈
        当前节点更新为其右节点
    }
}

程序源码:

int preorder2(node *root) {
    node *ptr=root;
    node *stack[1024]={NULL};
    int top=0;//top of stack
    while (ptr || top>0){
        if (ptr) {
            stack[top++]=ptr;//push
            printf("%c ",ptr->ch);//visit root
            num++;
            ptr=ptr->left;
        }
        else {
            ptr=stack[--top]; //ptr=stack top;pop;
            ptr=ptr->right;

        }
    }
    return num;
}

中序遍历

中序遍历要求先访问左子树再访问根节点最后访问右子树。 对于二叉查找树中序遍历将会得到一个有序序列。由定义给出递归遍历:

int inorder(node *root) {
    if (root != NULL) {
        inorder(root->left);
        printf("%c ",root->ch);
        inorder(root->right);
        num++;
    }
     return num;
}

模拟调用栈的行为,得到非递归遍历算法:

初始化,根节点入栈
当前节点置为根节点
当前节点不空或栈不空时执行循环 {
    //将当前节点子树的最左子树压栈
    若当前节点不空 {
        当前节点入栈
        当前节点更新为其左节点
    }
    否则 {
        当栈顶作为当前节点,访问当前节点
        弹栈
        当前节点更新为右子节点
    }
}

中序遍历栈的行为与前序遍历及其类似,只不过前序遍历在压栈时访问当前节点,中序遍历在弹栈时访问当前节点。

程序源码:

int inorder2(node *root) {
    node *ptr=root;
    node *stack[1024]={NULL};
    int top=0;//top of stack
    while (ptr || top>0) {
        if (ptr) {
            stack[top++]=ptr;//push
            ptr=ptr->left;
        }
        else {
            ptr=stack[--top];//ptr=stack top;pop;
            printf("%c ",ptr->ch);//visit root
            num++;
            ptr=ptr->right;
        }
    }
    return num;
}

后序遍历

后序遍历的递归过程可由定义给出

程序源码:

int postorder(node *root) {
    if (root != NULL) {
        postorder(root->left);
        postorder(root->right);
        printf("%c ",root->ch);
        num++;
    }
     return num;
}

因为需要先遍历两棵子树再访问根节点,后序遍历的非递归略为复杂,需要另外定义变量标记上一个访问过的节点。

当前节点不空或栈不空时执行循环 {
    //将当前节点的最左侧子树压栈
    当前节点不空时执行循环 {
        当前节点入栈
        当前节点更新为左子节点
    }
    当前节点置为栈顶
    若当前节点右子节点为空或右子节点是上一访问节点 {
        访问当前节点
        弹栈
        上一访问节点置为当前节点
        当前节点置为空节点
    }
    否则 {
        当前节点更新为右子节点
    }
}

程序源码:

int postorder2(node *root) {
    node *ptr=root;
    node *stack[1024]={NULL};
    node *previsited=NULL; //The last node has been visited
    int top=0;//top of stack
    while (ptr || top > 0) {
        while (ptr) {
            stack[top++]=ptr;//push
            ptr=ptr->left;
        }
        ptr=stack[top-1];
        if (ptr->right==NULL || ptr->right == previsited) {
            printf("%c ",ptr->ch);//visit root
            num++;
            previsited=ptr;
            top--;
            ptr = NULL;
        }
        else {
            ptr = ptr -> right;
        }
    }
    return num;
}

层序遍历

按照行优先顺序,从上到下从左到右访问二叉树。层序遍历的实现需要使用队列,程序流程如下:

若根节点不空 {
    则访问根节点
    根节点入队
}
当队不空时执行循环 {
    当前节点置为队首,弹出队首
    若左子节点不空 {
        访问左子节点
        左子节点入队
    }
    // 两个条件语句并列,绝不能加else
    若右子节点不空 {
        访问右子节点
        右子节点入队
    }
}

程序源码:

int levelorder(node *root) {
    node *ptr=root;
    node *queue[1024]={NULL};
    int front=-1,back=0;//(front,back)
    if (ptr) {
        printf("%c ",ptr->ch);
        num++;
        queue[back++]=ptr;//queue.push(ptr);
    }
    while (back > front+1) {//queue.empty()==false;
        ptr=queue[++front];//ptr=queue.front();queue.pop();
        if (ptr->left) {
            printf("%c ",ptr->left->ch);
            num++;
            queue[back++]=ptr->left;//queue.push(ptr->left);
        }
        if (ptr->right) {
            printf("%c ",ptr->right->ch);
            num++;
            queue[back++]=ptr->right;//queue.push(ptr->left);
        }
    }
    return num;
}

二叉树求深

二叉树的深度可以递归定位为左右子树深度最大值加一(根节点深度),由递归定义给出递归求深算法。

程序源码:

int depth(node *root) {
    int d1,d2;
    if (!root) {
        return 0;
    }
    d1=depth(root->left);
    d2=depth(root->right);
    return ((d1>d2)?d1:d2) + 1;
}   

前序或中序非递归遍历算法提供了一种沿各分支的遍历方式,结合回溯法思想即可得到一种非递归求深的方法。定义当前深度cur,已知最大深度dep并初始化为0;当遍历访问一个子节点时cur++,当某一分支遍历完成时更新dep;此时,根据遍历算法应该弹栈,同时cur--(即回溯过程)。此程序流程如下:

当前节点不空或栈不空时执行循环 {
    若当前节点不空 {
        当前节点入栈
        当前节点更新为其左节点
        cur++;
    }
    否则 {
    更新dep;
    弹栈
    cur--;
    当前节点更新为其右节点
    }
}
返回dep

程序源码:

int depth2(node *root) {
    node *ptr=root;
    node *stack[1024]={NULL};
    int top=0,dep=0,cur=0;//top of stack
    while (ptr || top>0){
        if (ptr) {
            stack[top++]=ptr;//push
            cur++;
            ptr=ptr->left;
        }
        else {
            if (cur > dep) { //update dep
                dep=cur;
            }
            cur--;
            ptr=stack[--top]; //ptr=stack top;pop;
            ptr=ptr->right;

        }
    }
    return dep;
}

二叉树还原

只有通过先序中序遍历序列或者后序中序遍历序列才可以确定一个二叉树,通过先序和后序无法还原出二叉树。

还原二叉树由递归算法来实现:

typedef Node* NodePtr;
void creat(NodePtr *rootPtr,char *mid, char * post, int mleft, int mright,int pleft, int pright) {

    *rootPtr = (Node *)malloc( sizeof(Node) );
    NodePtr root = *rootPtr;
    root->ch  = post[pright];
    root->left = NULL;
    root->right = NULL;

    int pos = mleft;
    while (mid[pos] != post[pright]) {
        pos++;
    }
    int Len = pos - mleft;
    if (pos > mleft) {
        creat(&root->left, mid, post, mleft, pos-1, pleft, pleft + Len -1);
    }
    if (pos < mright) {
        creat(&root->right, mid, post, pos+1, mright, pleft + Len, pright - 1);
    }

}

二叉树操作完整程序源码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define M 32

typedef struct Node{
    char ch;
    struct Node *prior;
    struct Node *left;
    struct Node *right;
}Node;

int num=0;
inline int init_Node(Node *this){
    this->ch='\0';
    this->prior=NULL;
    this->left=NULL;
    this->right=NULL;
    return 0;
}

inline Node *append_Node(Node *prior_argu,char ch_argu) {
    Node *nptr;
    nptr=(Node *)malloc(sizeof(Node));
    init_Node(nptr);
    nptr->prior=prior_argu;
    nptr->ch=ch_argu;
    if (ch_argu < prior_argu->ch) {
        prior_argu->left=nptr;
    }
    else if (ch_argu > prior_argu->ch) {
        prior_argu->right=nptr;
    }
    return nptr;
};

//using root ptr,and the root saves data too. using nullptr as the end.
inline int examp_tree(Node *root) {
    root->ch='m';
    //1st sub tree
    append_Node(root,'f');
    append_Node(root,'s');
    //2nd sub tree
    append_Node(root->left,'d');
    append_Node(root->left,'i');
    append_Node(root->right,'p');
    append_Node(root->right,'v');
    //3rd sub tree
    append_Node(root->left->left,'b');
    append_Node(root->left->left,'e');
    append_Node(root->left->right,'g');
    append_Node(root->left->right,'k');
    append_Node(root->right->left,'n');
    append_Node(root->right->left,'q');
    append_Node(root->right->right,'t');
    append_Node(root->right->right,'w');
    return 15;
}

int preorder(Node *root) {
    if (root != NULL) {
        printf("%c ",root->ch);
        preorder(root->left);
        preorder(root->right);
        num++;
    }
     return num;
}

int preorder2(Node *root) {
    Node *ptr=root;
    Node *stack[1024]={NULL};
    int top=0;//top of stack
    while (ptr || top>0){
        if (ptr) {
            stack[top++]=ptr;//push
            printf("%c ",ptr->ch);//visit root
            num++;
            ptr=ptr->left;
        }
        else {
            ptr=stack[--top]; //ptr=stack top;pop;
            ptr=ptr->right;

        }
    }
    return num;
}

int inorder(Node *root) {
    if (root != NULL) {
        inorder(root->left);
        printf("%c ",root->ch);
        inorder(root->right);
        num++;
    }
     return num;
}

int inorder2(Node *root) {
    Node *ptr=root;
    Node *stack[1024]={NULL};
    int top=0;//top of stack
    while (ptr || top>0) {
        if (ptr) {
            stack[top++]=ptr;//push
            ptr=ptr->left;
        }
        else {
            ptr=stack[--top];//ptr=stack top;pop;
            printf("%c ",ptr->ch);//visit root
            num++;
            ptr=ptr->right;
        }
    }
    return num;
}

int postorder(Node *root) {
    if (root != NULL) {
        postorder(root->left);
        postorder(root->right);
        printf("%c ",root->ch);
        num++;
    }
     return num;
}

int postorder2(Node *root) {
    Node *ptr=root;
    Node *stack[1024]={NULL};
    Node *previsited=NULL;//The last Node has been visited
    int top=0;//top of stack
    while (ptr || top > 0) {
        while (ptr) {
            stack[top++]=ptr;//push
            ptr=ptr->left;
        }
        ptr=stack[top-1];
        if (ptr->right==NULL || ptr->right == previsited) {
            printf("%c ",ptr->ch);//visit root
            num++;
            previsited=ptr;
            top--;
            ptr = NULL;
        }
        else {
            ptr = ptr -> right;
        }
    }
    return num;
}

/*int postorder3(Node *root) { //postorder traver wihth two stacks
    Node *ptr=root;
    Node *stack1[1024]={NULL},*stack2[1024]={NULL};
    int top1=0,top2=0;//top of stack
    stack1[top1++]=ptr;//push into stack1
    while (top1 > 0) {
        ptr=stack1[--top1];//ptr = stack1.top();stack1.pop()
        stack2[top2++]=ptr;//stack2.push(ptr)
        if (ptr->left) {
            stack1[top1++]=ptr->left;//stack1.push(ptr->left)
        }
        if (ptr->right) {
            stack2[top2++]=ptr->right;//stack2.push(ptr->right)
        }
    }
     while (top2 > 0) {
            printf("%c ",stack2[--top2]->ch);//stack2.top();stack2.pop();
            num++;
        }
    return num;
}*/

int levelorder(Node *root) {
    Node *ptr=root;
    Node *queue[1024]={NULL};
    int front=-1,back=0;//(front,back)
    if (ptr) {
        printf("%c ",ptr->ch);
        num++;
        queue[back++]=ptr;//queue.push(ptr);
    }
    while (back > front+1) {//queue.empty()==false;
        ptr=queue[++front];//ptr=queue.front();queue.pop();
        if (ptr->left) {
            printf("%c ",ptr->left->ch);
            num++;
            queue[back++]=ptr->left;//queue.push(ptr->left);
        }
        if (ptr->right) {
            printf("%c ",ptr->right->ch);
            num++;
            queue[back++]=ptr->right;//queue.push(ptr->left);
        }
    }
    return num;
}

int depth(Node *root) {
    int d1,d2;
    if (!root) {
        return 0;
    }
    d1=depth(root->left);
    d2=depth(root->right);
    return ((d1>d2)?d1:d2) + 1;
}

typedef Node* NodePtr;
void creat(NodePtr *rootPtr,char *mid, char * post, int mleft, int mright,int pleft, int pright) {

    *rootPtr = (Node *)malloc( sizeof(Node) );
    NodePtr root = *rootPtr;
    root->ch  = post[pright];
    root->left = NULL;
    root->right = NULL;

    int pos = mleft;
    while (mid[pos] != post[pright]) {
        pos++;
    }
    int Len = pos - mleft;
    if (pos > mleft) {
        creat(&root->left, mid, post, mleft, pos-1, pleft, pleft + Len -1);
    }
    if (pos < mright) {
        creat(&root->right, mid, post, pos+1, mright, pleft + Len, pright - 1);
    }

}

void test(void)
{
    int n;
    Node *root;
    root=(Node *)malloc(sizeof(Node));
    examp_tree(root);

    printf("preorder\n");
    num=0;
    n=preorder(root);
    printf("%d\n",n);
    printf("preorder2\n");
    num=0;
    n=preorder2(root);
    printf("%d\n",n);

    printf("inorder\n");
    num=0;
    n=inorder(root);
    printf("%d\n",n);
    printf("inorder2\n");
    num=0;
    n=inorder2(root);
    printf("%d\n",n);

    printf("postorder\n");
    num=0;
    n=postorder(root);
    printf("%d\n",n);
    printf("postorder2\n");
    num=0;
    n=postorder2(root);
    printf("%d\n",n);

    printf("levelorder\n");
    num=0;
    n=levelorder(root);
    printf("%d\n",n);

    n=depth(root);
    printf("depth:%d\n",n);

    char mid[M], post[M];
    gets(mid);
    gets(post);
    creat(&root, mid, post, 0, strlen(mid)-1, 0, strlen(post)-1);
    preorder(root);
}

int main() {
    test();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值