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
测试结果,没有内存泄露: