C语言二叉树操作集合:递归遍历、非递归遍历、层次遍历

二叉树的主要性质

1.一棵非空二叉树的第i层最多有 2 i 2^i (i >= 1)个节点
2.一棵深度为k的二叉树最多有 2 k 2^k -1个节点
3.一棵非空二叉树,设叶子节点数为 n 0 n_0 ,度为2的节点数为 n 2 n_2 ,则 n 0 n_0 = n 2 n_2 + 1
4.设有n个节点的完全二叉树的深度为k,则 k = [ l o g 2 n log_2n ] + 1 ([ l o g 2 n log_2n ]表示不大于 l o g 2 n log_2n 的最大整数)

1.递归遍历

在这里插入图片描述

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

typedef char elemType;
typedef struct BiTNode
{
    elemType data;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

BiTree createBiTree();                    /* 先序输入节点的值,构造二叉树 */
void PreOrderTraverse(BiTree T);          /* 先序遍历二叉树 */
void InOrderTraverse(BiTree T);           /* 中序遍历二叉树 */
void PostOrderTraverse(BiTree T);         /* 后序遍历二叉树 */
void visit(elemType x);                   /* 输出元素x */
BiTNode *SearchNode(BiTree T, elemType x);/* 在以T为根节点的二叉树中查找元素为x的节点 */
int CountLeaf(BiTree T);                  /* 求二叉树叶子节点个数 */
int BiTreeHigh(BiTree T);                 /* 求二叉树的深度 */
int CountNodes(BiTree T);                 /* 求二叉树叶子节点个数 */

int main(void)
{
    BiTree root;

    printf("请按先序顺序输入节点值,输入‘#’代表节点为空:\n");
    root = createBiTree();

    printf("先序递归:");
    PreOrderTraverse(root);
    printf("\n");
    printf("中序递归:");
    InOrderTraverse(root);
    printf("\n");
    printf("后序递归:");
    PostOrderTraverse(root);
    printf("\n");

    BiTNode *temp = SearchNode(root, 'E');
    printf("查找出的节点元素为:%c\n", temp->data);

    int leaves1 = CountLeaf(root);
    printf("二叉树叶子节点总数为:%d\n", leaves1);

    int nodes = CountNodes(root);
    printf("二叉树节点总数为:%d\n", nodes);

    int high = BiTreeHigh(root);
    printf("二叉树深度为:%d\n", high);

    return 0;
}

/* 先序输入节点的值,构造二叉树 */
BiTree createBiTree()
{
    char ch; BiTree T;

    if ((ch = getchar()) == '#') T = NULL;
    else
    {
        T = (BiTNode *)malloc(sizeof(BiTNode));
        T->data = ch;
        T->lchild = createBiTree();
        T->rchild = createBiTree();
    }

    return T;
}
/* 输出元素x */
void visit(elemType x)
{
    printf("%c, ", x);
}
/* 先序遍历二叉树 */
void PreOrderTraverse(BiTree T)
{
    if (T != NULL)
    {
        visit(T->data);
        PreOrderTraverse(T->lchild);
        PreOrderTraverse(T->rchild);
    }
}
/* 中序遍历二叉树 */
void InOrderTraverse(BiTree T)
{
    if (T)
    {
        InOrderTraverse(T->lchild);
        visit(T->data);
        InOrderTraverse(T->rchild);
    }
}
/* 后序遍历二叉树 */
void PostOrderTraverse(BiTree T)
{
    if (T)
    {
        PostOrderTraverse(T->lchild);
        PostOrderTraverse(T->rchild);
        visit(T->data);
    }
}
/* 在以T为根节点的二叉树中查找元素为x的节点 */
BiTNode *SearchNode(BiTree T, elemType x)
{
    if (!T) return NULL;

    if (T->data == x) return T;
    else
    {
        BiTNode *temp;
 
        temp = SearchNode(T->lchild, x);
        if(!temp){
            return SearchNode(T->rchild, x);
        }
     
        return temp;
    }

    return NULL;
}

/* 求二叉树的深度 */
int BiTreeHigh(BiTree T)
{
    int lh, rh, h;

    if (T == NULL) h = 0;
    else
    {
        lh = BiTreeHigh(T->lchild);
        rh = BiTreeHigh(T->rchild);
        h = (lh > rh ? lh : rh) + 1;
    }

    return h;
}
/* 求二叉树叶子节点个数 */
int CountLeaf(BiTree T)
{
    if (T == NULL) return 0;
    else if ((T->lchild == NULL) && (T->rchild == NULL)) return 1;
    else return (CountLeaf(T->lchild) + CountLeaf(T->rchild));
}
/* 求二叉树总节点个数 */
int CountNodes(BiTree T)
{
    if (T)
    {
        if ((T->lchild == NULL) && (T->rchild == NULL)) return 1;
        else return (CountNodes(T->rchild) + CountNodes(T->lchild) + 1);
    }

    return 0;
}


在这里插入图片描述

2.非递归遍历

2.1先序

分析:先序遍历的过程是首先访问根结点.然后先序遍历根的左子树,最后先序遍历根的右子树。对于根的左子树和右子树,遍历的过程相同。如果用非递归方法,就要在遍历左子树之前先保存右子树根结点的地址(指针),以便在完成左子树的遍历之后取出右子树根结点的地址,再遍历这棵右子树。同样,在遍历左子树的左子树之前也要先保存左子树的右子树根结点的地址,以此类推。可以看出,对这些地址的保存和取出符合后进先出的原则,可设置一个辅助栈来保存这些右子树根结点的地址。为了方便编写算法,这个辅助栈保存所有经过的结点的指针,包括空的根指针和空的孩子指针。

2.2中序

分析:中序遍历的过程是首先中序遍历左子树,然后访问根结点,最后中序遍历根的右子树。对于根的左子树和右子树,遍历的过程相同。如果用非递归方法,就要在遍历左子树之前先保存根结点的地址(指针),以便在完成左子树的遍历之后取出根结点的地址访问根结点,然后再中序遍历右子树。同样,在中序遍历左子树的左子树之前也要先保存左子树的根结点地址,以此类推。可以看出,对这些地址的保存和取出符合后进先出的原则,可设置一个辅助栈来保存所经过的结点的地址。为了方便编写算法,栈中也保存空树的空指针。中序遍历二叉树的非递归算法如下:

2.3后序

分析:后序遍历的过程是首先后序遍历左子树,然后后序遍历根的右子树,最后访问根结点。如果用非递归方法,就要在遍历左子树之前先保存根结点的地址,以便在完成左子树遍历之后根据根结点的地址遍历右子树和访问根结点。对于根的左子树和根的右子树,遍历的过程相同。对这些地址的保存和使用符合后进先出的原则,可设置-一个辅助栈来保存所经过的结点的地址。因为后序遍历的特点是只有在遍历了左子树和右子树之后才能访问根结点,所以为了表明子树是否被遍历过,可再设置一个辅助变量。

2.4层次遍历

由层次遍历的定义可以推知,在进行层次遍历时,对一层结点访问完后再按照它们的访问次序对各个结点的左孩子和右孩子顺序访问,这样一层一层地进行,先遇到的结点先访问,这与队列的操作原则比较吻合。因此,在进行层次遍历时可设置一个队列结构,遍历从二叉树的根结点开始,首先将根结点指针入队列,然后从队头取出一个元素,每取一个元素执行下面两个操作:

(1)访问该元素所指的结点;

(2)若该元素所指结点的左、右孩子指针非空,则将该元素所指结点的非空左孩子指针和右孩子指针顺序入队。

若队列非空,重复以上过程,当队列为空时,二叉树的层次遍历结束。在下面的层次遍历算法中,二叉树以二叉链表存储,一 维数组Queue[ MAXNODE ]用于实现队列,变量front和rear分别表示当前队列首元素和队列尾元素在数组中的位置。

在这里插入图片描述

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

typedef char elemType;
typedef struct BiTNode
{
    elemType data;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
BiTree createBiTree();                    /* 先序输入节点的值,构造二叉树 */
void PreOrderTraverseNonRec(BiTree T);    /* 先序非递归遍历二叉树 */
void InOrderTraverseNonRec(BiTree T);     /* 中序非递归遍历二叉树 */
void PostOrderTraverseNonRec(BiTree T);   /* 后序非递归遍历二叉树 */
void LevelOrderTraverse(BiTree T);        /* 层次遍历二叉树(从上到下,每层从左到右) */

#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef struct StackNode
{
    BiTNode **top;
    BiTNode **base;
    int size;
}Stack;
Stack *InitStack();            /* 初始化空栈 */
int isEmpty(Stack *S);         /* 判断栈空 */
BiTNode *GetTop(Stack *S);     /* 取栈顶数据 */
int push(Stack *S, BiTNode *p);/* 入栈 */
BiTNode *pop(Stack *S);        /* 出栈 */

#define MAXSIZE 100
typedef struct
{
    BiTNode *data[MAXSIZE];    /* 指针数组,数组中的每一个元素指向BiTNode类型的节点 */
    int front, rear;           /* front:指向队列中第一个元素
                                  rear:指向队列中最后一个元素下一位置
                                */
}Queue;


int main(void)
{
    BiTree root;

    printf("请按先序顺序输入节点值,输入‘#’代表节点为空:\n");
    root = createBiTree();


    printf("先序非递归:");
    PreOrderTraverseNonRec(root);
    printf("\n");
    printf("中序非递归:");
    InOrderTraverseNonRec(root);
    printf("\n");
    printf("后序非递归:");
    PostOrderTraverseNonRec(root);
    printf("\n");
    printf("按层次遍历:");
    LevelOrderTraverse(root);
    printf("\n");

    return 0;
}

/* 先序输入节点的值,构造二叉树 */
BiTree createBiTree()
{
    char ch; BiTree T;

    if ((ch = getchar()) == '#') T = NULL;
    else
    {
        T = (BiTNode *)malloc(sizeof(BiTNode));
        T->data = ch;
        T->lchild = createBiTree();
        T->rchild = createBiTree();
    }

    return T;
}
/* 输出元素x */
void visit(elemType x)
{
    printf("%c, ", x);
}
Stack *InitStack()
{
    Stack *S;

    S = (Stack *)malloc(sizeof(Stack));
    S->base = (BiTNode **)calloc(STACK_INIT_SIZE, sizeof(BiTNode *));
    S->top = S->base;
    S->size = STACK_INIT_SIZE;

    return S;
}
/* 取栈顶数据 */
BiTNode *GetTop(Stack *S)
{
    BiTNode *p = NULL;
    if (S->top == S->base) return NULL;
    p = *(S->top - 1);

    return p;
}
/* 入栈 */
int push(Stack *S, BiTNode *p)
{
    if (S->top - S->base >= S->size)
    {
        S->base = (BiTNode **)realloc
                    (S->base, (STACKINCREMENT + STACK_INIT_SIZE) * sizeof(BiTNode *));
        if (!S->base)
        {
            printf("入栈失败!\n");
            return 0;
        }
        S->top = S->base + S->size;
        S->size += STACKINCREMENT;
    }
    *S->top++ = p;

    return 1;
}
/* 出栈 */
BiTNode *pop(Stack *S)
{
    if (S->top == S->base)
    {
        printf("出栈失败!\n");
        return NULL;
    }
    return *--S->top;
}
/* 判断栈空 */
int isEmpty(Stack *S)
{
    return S->top == S->base;
}

/* 先序非递归遍历二叉树 */
void PreOrderTraverseNonRec(BiTree T)
{
    Stack *S = InitStack();
    BiTNode *p;

    push(S, T);
    while (!isEmpty(S))
    {
        p = GetTop(S);
        while (p != NULL)       //向左走到尽头
        {
            visit(p->data);
            push(S, p->lchild);
            p = GetTop(S);
        }

        /* while (p = GetTop(S))//向左走到尽头
        {
            visit(p->data);
            push(S, p->lchild);
        } */

        p = pop(S);             //空指针退栈
        if (!isEmpty(S))
        {
            p = pop(S);
            push(S, p->rchild); //向右一步
        }
    }
}
/* 中序非递归遍历二叉树 */
void InOrderTraverseNonRec(BiTree T)
{
    Stack *S = InitStack();
    BiTNode *p;

    push(S, T);
    while (!isEmpty(S))
    {
        p = GetTop(S);
        while (p != NULL)       //向左走到尽头
        {
            push(S, p->lchild);
            p = GetTop(S);
        }

        pop(S);                 //空指针退栈
        if (!isEmpty(S))
        {
            p = pop(S);
            visit(p->data);
            push(S, p->rchild);
        }
    }
}
/* 后序非递归遍历二叉树 */
void PostOrderTraverseNonRec(BiTree T)
{
    Stack *S = InitStack();
    BiTNode *p = T, *q;         /* q指向最近被访问过的节点,用作标志 */

    do{
        while (p)               //向左走到尽头
        {
            push(S, p);
            p = p->lchild;
        }
        q = NULL;
        while (!isEmpty(S))
        {
            p = GetTop(S);
            if ((p->rchild == NULL) || q == p->rchild)
            {                   //右子树为空或已经访问过
                visit(p->data);
                q = p;
                pop(S);
            }
            else                /* 右子树非空且未被遍历 */
            {
                p = p->rchild;  //向右一步
                break;
            }
        }
    }while (!isEmpty(S));
}
/* 层次遍历二叉树(从上到下,每层从左到右) */
void LevelOrderTraverse(BiTree T)
{
    Queue Q;
    BiTNode *temp = NULL;

    Q.front = Q.rear = 0;
    if (T)
    {
        Q.data[Q.rear] = T;
        Q.rear = (Q.rear + 1) % MAXSIZE;
        while (Q.front != Q.rear)
        {
            temp = Q.data[Q.front];
            visit(temp->data);
            Q.front = (Q.front + 1) % MAXSIZE;

            if (temp->lchild)
            {
                Q.data[Q.rear] = temp->lchild;
                Q.rear = (Q.rear + 1) % MAXSIZE;
            }
            if (temp->rchild)
            {
                Q.data[Q.rear] = temp->rchild;
                Q.rear = (Q.rear + 1) % MAXSIZE;
            }
        }
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42250302/article/details/89425105
今日推荐