二叉树的非递归遍历

二叉树遍历的三种递归遍历的方法都比较直观,这里说一下他们的三种非递归遍历的方法。

一.先序遍历的非递归算法

1.栈stack初始化

2.循环直到栈stack为空且p为空

           (1)如果p非空则

                    a.访问p所指的当前根节点;

                    b.将p值保存到栈stack中;

                    c.将p指向其左子树的根节点,继续遍历其左子树;

           (2)如果p为空则

                    a.从栈stack中退出元素赋给p,回溯至父亲节点

                    b.将p指向其右子树的根节点,继续遍历右子树

二.中序遍历的非递归算法

  1.栈stack初始化

  2.循环直到栈stack为空且p为空

             (1)如果p非空则

                     a.将p值保存到栈stack中

                     b.将p指向其左子树的根节点,继续遍历其左子树

             (2)如果p为空则

                     a.从栈stack中退出元素赋给p,回溯至父亲节点

                     b.访问p所指向的当前节点

                     c.将p指向其右子树,继续遍历其右子树

三.后序遍历的非递归算法

       在后序遍历算法中,在访问完左子树后,需要跳到右子树,右子树访问完毕了在回溯到根节点并访问它。因此,在后序遍历过程中,节点需要入栈俩次,并出栈俩次。第一次出栈只遍历完左子树,第二次出栈表示遍历完右子树。

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

typedef struct node {
    int data;
    struct node *left;
    struct node *right;
    int tag; //tag标志,用来来后序遍历的时候标记左子树和右子树被访问过。
} Node;

Node stack[100];//用来保存访问过的节点,相当于栈。

Node *make_node(int data)
{
    Node *pn = (Node *)malloc(sizeof(Node));
    pn->data = data;
    pn->left = NULL;
    pn->right = NULL;

    return pn;
}

Node *make_tree(Node *root, int data)
{
    if (root == NULL) {
        root = make_node(data);
    }
    else if (data < root->data)
        root->left = make_tree(root->left, data);
    else 
        root->right = make_tree(root->right, data);

    return root;
}

void preorder(Node *pn)
{
    if (pn) {
        printf("%d ", pn->data);
        preorder(pn->left);
        preorder(pn->right);
    }
}

//前序遍历的时候先是根节点,然后是左子树,最后是右子树
void preorder1(Node *pn) 
{
    memset(stack, 0, sizeof(stack));//栈清空
    int top = -1;//栈顶置为-1

    while (top != -1 || pn != NULL) {
        while (pn != NULL) {
            printf("%d ", pn->data);//访问根节点
            stack[++top] = *pn;//入栈,保存当前节点到栈中
            pn = pn->left;//继续访问左子树
        }
        if (top != -1) {
            pn = &stack[top--];//回溯到父亲节点
            pn = pn->right;//访问右子树
        }
    }
}

void inorder(Node *pn)
{
    if (pn) {
        inorder(pn->left);
        printf("%d ", pn->data);
        inorder(pn->right);
    }
}

//中序遍历的顺序先是左子树,然后根节点,最后是右子树
void inorder1(Node *pn)
{
    memset(stack, 0, sizeof(stack));
    int top = -1;

    while (top != -1 || pn != NULL) {
        while (pn != NULL) {
            stack[++top] = *pn;//左子树依次入栈
            pn = pn->left;
        }
        if (top != -1) {
            pn = &stack[top--];//回溯至父亲节点
            printf("%d ", pn->data);
            pn = pn->right;//访问右子树
        }
    }
}

void postorder(Node *pn)
{
    if (pn) {
        postorder(pn->left);
        postorder(pn->right);
        printf("%d ", pn->data);
    }
}
//后序遍历的顺序先是左子树,然后右子树,最后根节点。所以在访问完左子树后,需要跳到右子树,右子树访问完了再回溯至根节点
//在访问它,因此在后序遍历的过程中需要入栈和出栈俩次,所以需要一个额外的标志。
void postorder1(Node *pn)
{
    memset(stack, 0, sizeof(stack));
    int top = -1;

    do {
        while (pn != NULL) {
            stack[++top] = *pn;//入栈,保存当前节点
            pn->tag = 1;//表示左子树已经访问了
            pn = pn->left;
        }
        while((top != -1) && (stack[top].tag == 2)) {//tag == 2表示左子树和右子树都访问了,可以打印节点的信息了
            printf("%d ", stack[top].data);
            top--;
        }
        if (top != -1) {
            pn = &stack[top];//出栈回溯至父亲节点
            pn->tag = 2;//表示该节点的右子树访问过了
            pn = pn->right;
        }
    } while (top != -1);//栈为空就终止,
}

int main(void)
{
    int a[] = {10, 6, 4, 8, 5, 20, 18, 25, 28};
    Node *root = NULL;
    int i;

    for (i = 0; i < 9; i++)
        root = make_tree(root, a[i]);
    preorder(root);

    printf("\n");

    preorder1(root);

    printf("\n");

    inorder(root);
    printf("\n");
    inorder1(root);
    printf("\n");
    postorder(root);
    printf("\n");
    postorder1(root);
    printf("\n");
    return 0;
}
posted @ 2013-10-10 20:58  露天坝  阅读(253)  评论(0编辑  收藏  举报