二叉树遍历之递归、父结点、栈

二叉树结构

struct bst_node {
    struct bst_node *parent;
    struct bst_node *left;
    struct bst_node *right;
    int key;
};
1. 递归

这个代码最直接,不需要父结点,不多解释

void bst_inorder(struct bst_node *node)
{
    if (node) {
        if (node->left)
            bst_inorder(node->left);
        printf("%d ", node->key);
        if (node->right)
            bst_inorder(node->right);
    }
}

void bst_postorder(struct bst_node *node)
{
    if (node) {
        if (node->left)
            bst_postorder(node->left);
        if (node->right)
            bst_postorder(node->right);
        printf("%d ", node->key);
    }
}

void bst_preorder(struct bst_node *node)
{
    if (node) {
        printf("%d ", node->key);
        if (node->left)
            bst_preorder(node->left);
        if (node->right)
            bst_preorder(node->right);
    }
}

2. 父结点

使用父结点回溯

2.1中序

void bst_nonrecur_inorder(struct bst_node *root)
{
    struct bst_node *p = root, *parent;

    while (p && p->left)
        p = p->left;
    
    for (;p;) {
        printf("%d ", p->key);
        if (p->right) {
            p = p->right;
            while (p && p->left)
                p = p->left;
        }
        else {
            parent = p->parent;
            while (parent) {
                if (parent->left == p) {
                    break;
                }
                p = parent;
                parent = parent->parent;
            }
            p = parent;
        }
    }
}

遍历过程很简单,首先找到最左结点,即找根结点树的最小结点。

访问当前结点,如果当前结点有右子结点,则访问其右子结点的最左结点;否则回溯,直到某个祖先结点为其父结点的左子结点,此为后继结点;然后继续循环。

这个遍历过程就是算法导论中的两个过程,一个是取最小值,另一个是找后继结点


2.2 前序
void bst_nonrecur_preorder(struct bst_node *root)
{
    struct bst_node *p = root, *parent;

    while(p) {
        printf("%d ", p->key);
        if (p->left)
            p = p->left;
        else if (p->right)
            p = p->right;
        else {
            for (;;) {
                if (!(parent = p->parent)) {
                    p = NULL;
                    break;
                }
                if (parent->left == p && parent->right) {
                    p = parent->right;
                    break;
                }
                p = parent;
            }
        }
    }
}
前序遍历最简单,首先访问当前结点,然后优先访问左子树,然后右子树,如果遇到叶子结点,则回溯,直到其祖先结点为其父结点的左子结点而且该祖先结点有右子树

2.3 后序

void bst_nonrecur_postorder(struct bst_node *root)
{
    struct bst_node *p = root, *parent;

    while(p) {
        if (p->left)
            p = p->left;
        else if (p->right)
            p = p->right;
        else {
            printf("%d ", p->key);
            for (;;) {
                if (!(parent = p->parent)) {
                    p = NULL;
                    break;
                }
                if (parent->left == p) {
                    if (parent->right) {
                        p = parent->right;
                        break;
                    }
                    printf("%d ", parent->key);
                }
                else if (parent->right == p) {
                    printf("%d ", parent->key);                    
                }
                p = parent;
            }
        }
    }
}

后序遍历类似前序,只不过优先访问左子树,其次右子树,最后才是根结点。上述代码先访问叶子结点,在回溯的过程中,分两种情况讨论:

1. 该叶子结点在其某祖先结点的左子树,如果该祖先结点没有右子树,则直接访问该祖先结点,否则转向其右子树

2. 该叶子结点在其某祖先结点的右子树,由于从右子树回溯,表示左右子树都访问过了,直接访问该祖先结点

3. 栈

使用数组实现栈结构

3.1 中序

void bst_stack_inorder(struct bst_node *root)
{
    int i = 0;
    struct bst_node *s[1024] = {0, };
    struct bst_node *p = root;

    /* use s[0] as NULL pointer */
#define PUSH_S(n, i) s[++(i)]=(n)
#define POP_S(i)     s[(i)--]

    PUSH_S(p, i);
    while (p->left) {
        PUSH_S(p->left, i);
        p = p->left;
    }

    while (i > 0) {
        /* pop top && visit top var */
        p = POP_S(i);
        printf("%d ", p->key);

        /* push right */
        if (p->right) {
            PUSH_S(p->right, i);
            p = p->right;
            /* push all left */
            while (p->left) {
                PUSH_S(p->left, i);
                p = p->left;
            }
        }
    }
}
遍历过程和使用父结点类似,只不过将回溯过程使用栈来处理

3.2 前序
void bst_stack_preorder(struct bst_node *root)
{
    int i = 0;
    struct bst_node *s[1024] = {0, };
    struct bst_node *p = root;

    /* use s[0] as NULL pointer */
#define PUSH_S(n, i) s[++(i)]=(n)
#define POP_S(i)     s[(i)--]

    PUSH_S(p, i);

    while (i > 0) {
        /* pop top && visit top var */
        p = POP_S(i);
        printf("%d ", p->key);

        /* push right */
        if (p->right) {
            PUSH_S(p->right, i);
        }
        /* push left */
        if (p->left)
            PUSH_S(p->left, i);
    }
}
此遍历过程最简单,首先访问当前结点,再将右子结点压栈,最后再压入左子结点,因为栈的后进先出原则保证优先访问左子结点

3.3 后序
static inline void bst_stack_post_push(struct bst_node *p, struct bst_node **s, int *i)
{
    int index = *i;
    s[++index] = p;
    while (p->left || p->right) {
        if (p->left) 
            p = p->left;
        else
            p = p->right;
        s[++index] = p;
    }
    *i = index;
}


void bst_stack_postorder(struct bst_node *root)
{
    int i = 0;
    struct bst_node *s[1024] = {0, };
    struct bst_node *p = root, *last;;

    /* use s[0] as NULL pointer */
#define PUSH_S(n, i) s[++(i)]=(n)
#define POP_S(i)     s[(i)--]
#define TOP_S(i)     s[(i)]

    bst_stack_post_push(p, s, &i);
    while (i > 0) {
        /* get top */
        p = TOP_S(i);
        if (p->left == last && p->right) {
            bst_stack_post_push(p->right, s, &i);
        }
        else {
            p = POP_S(i);            
            printf("%d ", p->key);
            last = p;
        }
    }
}

此遍历过程最复杂,优先访问左子结点,其次右子结点,一直到叶子结点,将其所有祖先结点入栈,这个过程由函数bst_stack_post_push完成

使用last 来保存上一个遍历的结点

遍历时,先查看栈顶元素,分两种情况讨论:

1.  如果遍历路径是从左子树到根,则查看根结点是否有右子树,如果有右子树,则需要先将右子树使用bst_stack_post_push处理,否则直接访问

2.  如果遍历路径是从右子树到根,则直接访问
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值