二叉树非递归先序、中序、后序遍历的完整C实现

本文提供了一种使用C语言实现二叉树非递归先序、中序和后序遍历的方法。通过引入栈来辅助实现,避免了传统递归方式带来的堆栈溢出风险。文章首先介绍了递归实现的基本思路,随后详细展示了非递归算法的具体步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.介绍

本文使用C语言完整实现二叉树的非递归先序、中序、后序遍历。

以下是原递归算法(二叉树的先序、中序、后序遍历的递归实现算法):

// 1.先序递归实现算法
void PreOrder(BiTree T) {
	if (T != NULL) {
		visit(T);
		PreOrder(T->lchild);
		PreOrder(T->rchild);
	}
}

// 2.中序递归实现算法
void InOrder(BiTree T) {
	if (T != NULL) {
		InOrder(T->lchild);
		visit(T);
		InOrder(T->rchild);
	}
}

// 3.后序递归实现算法
void PostOrder(BiTree T) {
	if (T != NULL) {
		PostOrder(T->lchild);
		PostOrder(T->rchild);
		visit(T);
	}
}

2.实现非递归完整版本(2021-04-06更)

栈和树的数据结构的头文件如下:

//
// Created by TAYNPG on 2021/4/5.
//

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


/*
 * --->>>>>>本头文件中的结构体及对应操作的约定:
 * 1.所有涉及判断是否执行成功的函数的返回值,统一规为返回 0 值成功,其他值 失败
 * 2.所有涉及真假的函数,返回值统一规为 1 真,0 值假
 *
 * */


#ifndef ONE_STRUCT_H
#define ONE_STRUCT_H

// **************************************
// 栈 -- 开始
// **************************************
// 栈内数据域类型
typedef void * StackDataType;
// 模拟栈结构
typedef struct Stack {
    StackDataType data;
    struct Stack* next;
}MyStack;

// 初始化栈顶指针(该指针也需要释放空间)。
int StackInit(MyStack **head) {

    *head = (MyStack *)malloc(sizeof(MyStack));

    if ((*head) != NULL) {
    
        (*head)->data = NULL;
        (*head)->next = NULL;
        
        return 0;
    }
    return -1;
}

// 入栈操作。
int StackPush(MyStack** head, StackDataType data) {

    MyStack* tmp = (MyStack *)malloc(sizeof(MyStack));
    if (tmp == NULL) {
        return -1;
    }

    tmp->next = (*head)->next;
    tmp->data = data;
    (*head)->next = tmp;

    return 0;
}

// 出栈操作, 并将值保存到 p。
int StackPop(MyStack** head, StackDataType* p) {

    if ((*head)->next == NULL) {
        return -1;
    }

    MyStack* tmp = (*head)->next;
    (*head)->next = tmp->next;
    *p = tmp->data;
    free(tmp);

    return 0;
}

// 栈是否为空,返回 1 为真。
int StackEmpty(MyStack* head) {
    if (!head || head->next == NULL)
        return 1;
    return 0;
}

// 查看栈顶结点的数据,不出栈。
StackDataType StackTop(MyStack* head) {

    if (head == NULL || head->next == NULL) {
        return NULL;
    }
    return head->next->data;
}

// 打印整个栈的数据,可复制出去修改后使用。
/*
void StackPrint(MyStack* head) {

    if (!head) return;
    MyStack* tmp = head->next;

    if (tmp == NULL) {
        printf("栈为空。\n");
        return ;
    }

    printf("打印内容的左侧为栈顶。\n");
    while (tmp != NULL) {
        printf("%d ", *(int *)tmp->data); // 修改这一行
        tmp = tmp->next;
    }

    printf("\n");
}
 */

// 释放栈空间。
void StackFree(MyStack* head) {

    if (!head) return;
    MyStack* ms = head->next;
    while(ms != NULL) {
        MyStack* temp = ms;
        ms = ms->next;
        free(temp);
    }
    free(head);
}
// **************************************
// 栈 -- 结束
// **************************************

// **************************************
// 树 -- 开始
// **************************************

// 树的数据域 数据类型
typedef void * TreeDataType ;

// 二叉树结构
typedef struct TreeNode {
    TreeDataType data;
    struct TreeNode* lchild;
    struct TreeNode* rchild;
}MyTree;

// 树结点的添加位置
enum Direction {
    LEFT = 1,
    RIGHT
}TreeDirection;

// 初始化树的根节点。
int TreeInit(MyTree** root, TreeDataType data) {

    *root = NULL;
    MyTree* tmp = (MyTree *)malloc(sizeof (MyTree));
    if (tmp == NULL) {
        return -1;
    }
    tmp->data = data;
    tmp->lchild = NULL;
    tmp->rchild = NULL;
    *root = tmp;

    return 0;
}

// 添加树结点,返回新结点的指针,如果返回值为NULL,表示添加失败。
// 失败原因可能为:
// 1.传入的父结点为空。
// 2.要添加的位置已经有结点。
// 3.传入的方向有错误。
MyTree* TreeAddNode(MyTree* parent, TreeDataType data, enum Direction direction) {

    if (parent == NULL) return NULL;
    MyTree* tmp = NULL;

    switch (direction) {
        case LEFT:
            if (parent->lchild != NULL) return NULL;
            tmp = (MyTree *)malloc(sizeof (MyTree));
            tmp->data = data;
            tmp->lchild = NULL;
            tmp->rchild = NULL;
            parent->lchild = tmp;
            break;

        case RIGHT:
            if (parent->rchild != NULL) return NULL;
            tmp = (MyTree *)malloc(sizeof (MyTree));
            tmp->data = data;
            tmp->lchild = NULL;
            tmp->rchild = NULL;
            parent->rchild = tmp;
            break;

        default:
            return NULL;
    }

    return tmp;
}

// 查看树结点的值,可复制出去修改后使用。
/*
void TreeNodeVisit(MyTree* root) {

    if (root == NULL) {
        printf("树为空。\n");
        return;
    }

    printf("%c ", *(char *)root->data); // 修改这一行
}
 */

// 释放树空间,请传入树的根结点完全释放。
void FreeTree(MyTree* root) {

    if (root != NULL) {
        FreeTree(root->lchild);
        FreeTree(root->rchild);
        free(root);
    }
}

// **************************************
// 树 -- 结束
// **************************************


#endif //ONE_STRUCT_H

main函数

下面在main函数中手动生成一个树,用于测试,树的结构如下:
在这里插入图片描述
按照上图所示,构造这样的一棵树:

#include "MyStruct.h"

// 这里写三个遍历,具体看下文。

int main()
{
    //  创建树
    // **********************************************
    printf("开始...\n");

    MyTree* root = NULL;
    char data[] = {'A', 'B', 'C', 'D', 'E'};
    TreeInit(&root, &data[0]);   // 根结点是 A

    // 在 A 的左边添加 B,返回 B 的位置
    MyTree* nodeB = TreeAddNode(root, (char *)&data[1], LEFT);
    TreeAddNode(nodeB, (char *)&data[3], LEFT);    // 在 B 的左边添加 D
    TreeAddNode(nodeB, (char *)&data[4], RIGHT);	  // 在 B 的右边添加 E
    TreeAddNode(root, (char *)&data[2], RIGHT);    // 在 A 的右边添加 C
    // ***********************************************

    printf("先序遍历:\n");
    PreOrder(root);  // 先序遍历
    printf("\n中序遍历\n");
    InOrder(root);  // 中序遍历
    printf("\n后序遍历\n");
    PostOrder(root);  // 后序遍历

    FreeTree(root);   // 释放树的内存
    printf("\n释放树空间完成。\n");

    return 0;
}

遍历算法

1.先序遍历

// 先序遍历
void PreOrder(MyTree* root) {

    MyStack* head = NULL;
    StackInit(&head);

    MyTree* p = root;

    while (p || !StackEmpty(head)) {

        if (p) {
            TreeNodeVisit(p);
            StackPush(&head, p);
            p = p->lchild;
        }
        else {
            StackPop(&head, (void *)&p);
            p = p->rchild;
        }
    }

    StackFree(head);
}

2.中序遍历

// 中序遍历
void InOrder(MyTree* root) {

    MyStack* head = NULL;
    StackInit(&head);

    MyTree* p = root;

    while (p || !StackEmpty(head)) {

        if (p) {
            StackPush(&head, p);
            p = p->lchild;
        }
        else {
            StackPop(&head, (void *)&p);
            TreeNodeVisit(p);   // 访问结点
            p = p->rchild;
        }
    }
    StackFree(head); // 释放栈
}

3.后序遍历

void PostOrder(MyTree* root) {

    MyStack* head = NULL;
    StackInit(&head);

    MyTree* p = root; // 临时访问指针
    MyTree* recent = NULL;  // 最近一次访问出栈的结点

    while (p || !StackEmpty(head)) {

        if (p) {
            StackPush(&head, p);
            p = p->lchild;  // 一直向左孩子走
        }
        else {
            // 没有左孩子了,就看栈顶结点有没有右孩子
            p = StackTop(head);
            if (p->rchild && p->rchild != recent) {

                p = p->rchild;
                StackPush(&head, p);
                p = p->lchild;
            }
            else {
                // 弹出栈顶的结点
                StackPop(&head, (void *)&p);
                TreeNodeVisit(p);
                recent = p;
                p = NULL; // 标记临时结点 指针 为空(再往后没有子结点了)
            }
        }
    }

    StackFree(head);
}

结果演示

下面运行结果:
在这里插入图片描述
搞定,最后用内存分析工具分析一下我们的程序有没有内存泄露:

 valgrind --tool=memcheck --leak-check=full ./a.out

测试结果,没有内存泄露:
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值