先序建立二叉树以及三种遍历(递归、非递归实现)

文章目录

  • 二叉树节点定义
  • 一、二叉树的建立
  • 二、二叉树的遍历
    • 1.递归实现
    • 2.非递归实现
  • 实现


二叉树的节点定义

typedef struct tree
{
    int a;
    struct tree* left;
    struct tree* right;
    int tag;//tag域用于后序遍历非递归
}Tnode, *Tree;

//使用堆栈及其操作的定义

typedef Tree* Stack;
static int top = -1;

void pop(Stack a)//出栈
{
    top--;
}
void push(Stack a,Tree p)//入栈
{
    a[++top] = p;
}

一、二叉树的建立

Tree precreate()//使用一级指针,采用递归先序创建二叉树
{
    int a;
    scanf_s("%d", &a);
    if (a == -1)//表示结束输入节点
        return NULL;
    else {
        Tree root = (Tree)malloc(sizeof(Tnode));
        root->a = a;
        root->left = precreate();
        root->right = precreate();
        return root;
    }
}
void create(Tree *T)//使用二级指针,无返回值,递归实现先序创建二叉树
{
    int a;
    scanf_s("%d", &a);
    if (a == -1)//表示结束输入节点
        *T = NULL;
    else
    {
        *T = (Tree)malloc(sizeof(Tnode));//给一级指针赋值
        (*T)->a = a;//*T要加括号,因为*优先级低于->
        create(&(*T)->left);//传入的是一级指针的地址,因为要改变一级指针所存的值(即树节点的地址)
        create(&(*T)->right);
    }
}

二、二叉树的遍历

1.递归

void preorder(Tree T)//先序遍历递归实现
{
    if (T) {
        printf("%d ", T->a);
        preorder(T->left);
        preorder(T->right);
    }
}
void midorder(Tree T)//中序遍历递归实现
{
    if (T) {
        midorder(T->left);
        printf("%d ", T->a);
        midorder(T->right);
    }
}
void postorder(Tree T)//后序遍历递归实现
{
    if (T) {
        postorder(T->left);
        postorder(T->right);
        printf("%d ", T->a);
    }
}

2.非递归

void PreOrderTraversal(Stack a, Tree p)//先序遍历非递归实现,根->左->右
{
    if (p) {
        push(a, p);
        while (top != -1) {//内层while退出后,说明左子树遍历完毕,取栈顶元素,同时令其出栈
            Tree q = a[top];
            pop(a);
            while (q) {//节点不空时,一直向其左子树走到尽头,同时碰到不空节点则输出,若有右儿子则压栈,至q==NULL退出内层while
                printf("%d ", q->a);
                if (q->right){
                    push(a, q->right);
                }
                q = q->left;
            }
        }
    }
}
void InOrderTraversal(Stack a, Tree p)//中序遍历非递归实现,左->根->右
{
    while (p || top != -1) {//当树不空或栈不空时进行遍历
        while (p) {//节点不空则将其压栈,并一直向左子树走,直至P==NULL,退出第一个while
            push(a, p);
            p = p->left;
        }
        if (top != -1) {//此时p==NULL,说明左子树压栈完成,栈不空则取栈顶元素,访问节点
            printf("%d ", a[top]->a);
            p = a[top]->right;//此时该元素左子树以及自身均被访问完,所以转向访问该节点的右儿子,并弹出该元素
            pop(a);
        }
    }
}
void PostOrderTraversal(Stack a, Tree p)//后序遍历非递归实现,设计一个tag标记是第几次经过该节点,左->右->根
{
    while (p || top != -1) {//当树不空或栈不空时进行遍历
        while (p) {//若节点不空,则将其压栈,设其tag=0,表示第一次访问该节点,此时将顺着该节点访问左子树
            push(a, p);
            p->tag = 0;
            p = p->left;
        }
        p = a[top];//p==NULL,重新取栈顶元素并弹栈
        pop(a);
        if (p->tag == 0 && p->right) {//此时设tag=1,表示已经访问完左子树,如果其有右孩子就访问其右子树并且要再次将该节点压栈
            p->tag++;
            push(a, p);//因为右孩子未访问完毕,不能访问根节点,所以需要重新压栈,并转向右子树遍历
            p = p->right;
        }
        else {//p->tag==1或者p->right==NULL情况,说明该节点的右子树全部访问完毕,可以输出该节点,并将其置为NULL,便于之后重新取未访问的栈顶元素
            printf("%d ", p->a);
            p = NULL;
        }
    }
}


完整代码测试(需加上之前定义的各种函数)

#include<stdio.h>
#include<stdlib.h>
#define MAXIMSIZE 100//定义堆栈大小
typedef struct tree//二叉树节点定义
{
    int a;
    struct tree* left;
    struct tree* right;
    int tag;
}Tnode, *Tree;
typedef Tree* Stack;
static int top = -1;

void create(Tree *T);
Tree precreate();
void preorder(Tree T);
void midorder(Tree T);
void postorder(Tree T);
void PreOrderTraversal(Stack a, Tree p);
void InOrderTraversal(Stack a, Tree p);
void PostOrderTraversal(Stack a, Tree p);

void pop(Stack a)//出栈
{
    top--;
}
void push(Stack a,Tree p)//入栈
{
    a[++top] = p;
}
/**********************************************主程序测试*****************************************/
int main(){
    Tree T1;
    create(&T1);//无返回值建树T1
    Tree T2 = precreate();//有返回值建树T2
    postorder(T1);//后序遍历T1
    printf("\n");
    Stack a[MAXIMSIZE];//建一个空堆栈
    PostOrderTraversal(a,T2);//中序遍历T2
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值