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

来源:互联网 发布:c语言数据类型有哪些 编辑:程序博客网 时间:2024/06/06 01:46

二叉树结构

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.  如果遍历路径是从右子树到根,则直接访问
原创粉丝点击