[数据结构] 二叉树的三种遍历方法(前序,中序,后序遍历)的递归/非递归实现方式.

前言

最近在复习数据结构,突然想起了之前用C和C++写的数据结构代码,现在将其发布到CSDN,方便日后查看。

更详细的代码和其他常用数据结构的实现在GitHub上,欢迎大家关注

https://github.com/VandarkhoIme/Algorithm

DataElement.h

定义了数据元素

#ifndef DATAELEMENT_H_INCLUDED
#define DATAELEMENT_H_INCLUDED

#define MAX_SIZE 255
//¶¨ÒåÊý¾ÝÔªËØ
typedef struct{
    int id;
    char name[255];
}ElementType;

#endif // DATAELEMENT_H_INCLUDED

BinaryLinkedList.h


//二叉链表(二叉树的链式存储结构)
#ifndef BINARYLINKEDLIST_H_INCLUDED
#define BINARYLINKEDLIST_H_INCLUDED
#include "DataElement.h"

//定义二叉链表结点的存储结构
typedef struct BiNode{
    int key;                    //按照完全二叉树的方法给树的结点编号
    int status;             //判断该结点是否成功创建
    ElementType data;           //数据域
    struct BiNode * lchild;     //左孩子(指针域)
    struct BiNode * rchild;     //右孩子(指针域)
}BiNode;

//以b为根节点,创建二叉树
void CreateBinaryLinkedList(BiNode * b);
//以下3个函数是CreateBinaryLinkedList的子函数,用来配合它使用的
void PreCreate(BiNode * b);     //前序创建
void InCreate(BiNode * b);      //中序创建
void PostCreate(BiNode * b);    //后序创建

//以某种顺序遍历二叉树
void TraverseBinaryTree(BiNode *b);
//以下6个函数是TraverseBinaryTree的子函数.
void PreOrderTraverse(BiNode *b);    //前序遍历
void InOrderTraverse(BiNode *b);     //中序遍历
void PostOrderTraverse(BiNode *b);   //后序遍历

void InOrderTraverse_Re(BiNode *b);     //中序遍历的非递归算法
void PreOrderTraverse_Re(BiNode *b);    //前序遍历的非递归算法
void PostOrderTraverse_Re(BiNode *b);   //后序遍历的非递归算法    注意 这里有坑。后序遍历的非递归算法比较复杂。暂不实现

#endif // BINARYLINKEDLIST_H_INCLUDED

BinaryLinkedList.c

相当于Java的interface

#include "BinaryLinkedList.h"
#include "LinkedStack.h"
#include <stdio.h>
#include <stdlib.h>

void CreateBinaryLinkedList(BiNode * b){
    printf("请选择创建方式(前序创建输入1,中序创建输入2,后序创建输入3):\n");
    int a;
    scanf("%d", &a);
    //开始创建二叉树
    if(a == 1){
        printf("开始前序创建二叉树\n");
        PreCreate(b);
    }

    else if(a == 2){
        printf("开始中序创建二叉树\n");
        InCreate(b);
    }

    else if(a == 3){
        printf("开始后序创建二叉树\n");
        PostCreate(b);
    }

    else{
        printf("输入的创建方式有误,无法创建");
        return;
    }
    printf("创建完成\n");
}

//三种创建二叉树(前序中序后序)的函数实现
void PreCreate(BiNode * b){
    ElementType e;
    printf("请输入结点的id(如果输入-1,那么就不在这个枝干上创建结点):\n");
    scanf("%d",&e.id);
    if(e.id == -1){     //如果用户输入了-1,那么将该结点的状态设为-999999,等函数返回之后释放该节点
        b->status = -999999;
        return;
    }


    printf("请输入结点的name:\n");
    scanf("%s",e.name);

    //以下
    b->data = e;    //将输入的结点信息赋值给当前结点的数据域
    b->lchild = (BiNode *)malloc(sizeof(BiNode));   //给左孩子指针分配内存
    PreCreate(b->lchild);                           //递归创建左子树
    if(b->lchild->status == -999999){    //如果创建失败,那么删除刚刚分配的左孩子的内存,并且将指针置空
        free(b->lchild);
        b->lchild = NULL;
    }

    b->rchild = (BiNode *)malloc(sizeof(BiNode));   //给右孩子指针分配内存
    PreCreate(b->rchild);                           //递归创建右子树
    if(b->rchild->status == -999999){    //如果创建失败,那么删除刚刚分配的右孩子的内存,并且将指针置空
        free(b->rchild);
        b->rchild = NULL;
    }
}
void InCreate(BiNode * b){

}
void PostCreate(BiNode * b){

}

//以下是遍历算法
void TraverseBinaryTree(BiNode *b){
    printf("请选择遍历方式(前序遍历输入1,中序遍历输入2,后序遍历输入3\n前序非递归遍历输入4,中序非递归遍历输入5,后序非递归遍历输入6):\n");
    int a;
    scanf("%d", &a);
    //开始遍历二叉树
    if(a == 1){
        printf("开始前序遍历二叉树\n");
        PreOrderTraverse(b);
    }

    else if(a == 2){
        printf("开始中序遍历二叉树\n");
        InOrderTraverse(b);
    }

    else if(a == 3){
        printf("开始后序遍历二叉树\n");
        PostOrderTraverse(b);
    }
    else if(a == 4){
        printf("开始前序非递归遍历二叉树\n");
        PreOrderTraverse_Re(b);
    }
    else if(a == 5){
        printf("开始中序非递归遍历二叉树\n");
        InOrderTraverse_Re(b);
    }
    else if(a == 6){
        printf("开始后序非递归遍历二叉树\n");
        PostOrderTraverse_Re(b);
    }

    else{
        printf("输入的遍历方式有误,无法遍历");
        return;
    }
    printf("遍历完成\n");
}
void PreOrderTraverse(BiNode *b){
    if(b == NULL)       //对应上面的一大串注释。这里就是通过if语句判断指针是否为NULL
        return;
    printf("%d\t%s\n",b->data.id,b->data.name);
    PreOrderTraverse(b->lchild);
    PreOrderTraverse(b->rchild);
}
void InOrderTraverse(BiNode *b){
    if(b == NULL)
        return;
    InOrderTraverse(b->lchild);
    printf("%d\t%s\n",b->data.id,b->data.name);
    InOrderTraverse(b->rchild);
}
void PostOrderTraverse(BiNode *b){
    if(b == NULL)
        return;
    PostOrderTraverse(b->lchild);
    PostOrderTraverse(b->rchild);
    printf("%d\t%s\n",b->data.id,b->data.name);
}


void InOrderTraverse_Re(BiNode *b){
    //中序遍历的非递归算法
    //王道考研数据结构 以中序遍历为例讲解算法思想。那么就先实现非递归的中序遍历。
    //算法思想:
        //1.初始时扫描根结点进栈,并扫描根结点的所有左侧结点并将它们一一进栈
        //2.出栈一个结点,访问它(在这里是打印它)
        //3.扫描该节点的右孩子结点将其进栈(只扫描它的右孩子!不是所有)
        //4.依次扫描右孩子结点的左侧结点并将它们一一进栈(其实就是第1步的重复。将右孩子结点看作了根结点)
        //5.反复该过程直到栈空为止。

    LinkedStack *s = (LinkedStack*)malloc(sizeof(LinkedStack));     //创建一个链栈
    InitLinkedStack(s);
    BiNode * temp = b;          //创建一个中间变量保存形参方便后续操作
    while(temp != NULL || s->length != 0){  //只要结点存在或者栈非空,那么就继续循环
        if(temp != NULL){
            Push(s,*temp);                  //第一步:让根节点进栈
            temp = temp->lchild;                    //通过循环让根节点的左孩子们进栈
        }
        else{                               //else代表结点空了
            BiNode * t = (BiNode*)malloc(sizeof(BiNode));
            Pop(s,t);                   //注意 这里不是将空结点扔出去。空结点根本就没有进栈
                                        //    这里是出栈一个真实的结点。
                                        //出栈真实的那个结点,存在了临时变量t里面

            printf("%d\t%s\n",t->data.id,t->data.name);
            temp = t->rchild;           //用一个临时变量t保存该结点,然后将temp赋值给t的右孩子
            free(t);                    //用完空间之后记得释放,并且将指针置空。避免产生野指针
            t = NULL;
        }
    }
    free(s);

}
void PreOrderTraverse_Re(BiNode *b){    //前序遍历的非递归算法

    //和中序遍历差不多。就是代码顺序有改变 我把注释都删了

    LinkedStack *s = (LinkedStack*)malloc(sizeof(LinkedStack));
    InitLinkedStack(s);
    BiNode * temp = b;
    while(temp != NULL || s->length != 0){
        if(temp != NULL){
            printf("%d\t%s\n",temp->data.id,temp->data.name);     //和中序遍历不同顺序。这里是先打印
            Push(s,*temp);
            temp = temp->lchild;
        }
        else{
            BiNode * t = (BiNode*)malloc(sizeof(BiNode));
            Pop(s,t);
            temp = t->rchild;
            free(t);
            t = NULL;
        }
    }
    free(s);
}
void PostOrderTraverse_Re(BiNode *b){   //后序遍历的非递归算法  注意 这里有坑。后序遍历的非递归算法比较复杂。暂不实现

    ;
}

LinkedStack.h

#ifndef LINKEDSTACK_H_INCLUDED
#define LINKEDSTACK_H_INCLUDED
//链栈
//这个链栈用来非递归遍历二叉树而特别制造
#include <stdio.h>
#include <stdlib.h>
#include "BinaryLinkedList.h"
//结点 注意data就是二叉树的结点
typedef struct StackNode{
    BiNode data;
    struct StackNode * next;
}StackNode;
//存放栈顶指针的数据结构。里面有栈顶指针和栈的length
typedef struct LinkedStack{
    StackNode * Top;
    int length;
}LinkedStack;

//初始化栈
void InitLinkedStack(LinkedStack * s);

//压栈   将元素e压入栈中     返回值int为0代表失败,1为成功。
int Push(LinkedStack * s, BiNode e);

//删除栈S中的栈顶元素,并用e来返回弹出的值     返回值int为0代表失败,1为成功。
int Pop(LinkedStack * s, BiNode * e);

//判断栈是否为空
void IsStackEmpty(LinkedStack * s);



#endif // LINKEDSTACK_H_INCLUDED

LinkedStack.c

#include "LinkedStack.h"
#include <stdlib.h>

//初始化栈
void InitLinkedStack(LinkedStack * s){
    s->Top = NULL;
    s->length = 0;
}


int Push(LinkedStack * s, BiNode e){
    StackNode * node = (StackNode *)malloc(sizeof(StackNode));     //创建一个结点,节点里存放新压入元素e的信息
    node->data = e;
    node->next = s->Top;
    s->Top = node;
    s->length++;
    return 1;
}


int Pop(LinkedStack * s, BiNode* e){
    *e = s->Top->data;
    StackNode * node = s->Top;
    s->Top = s->Top->next;
    free(node);
    node = NULL;
    s->length--;
    return 1;
}


void IsStackEmpty(LinkedStack * s){

}

main.c

主函数

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "DataElement.h"
#include "BinaryLinkedList.h"

void TestBinaryLinkedList();

int main()
{

    TestBinaryLinkedList();
    return 0;
}

void TestBinaryLinkedList(){
    BiNode * node = (BiNode *)malloc(sizeof(BiNode));
    CreateBinaryLinkedList(node);
    TraverseBinaryTree(node);
    free(node);
    node = NULL;

}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值