【数据结构】非递归实现二叉树的前 + 中 + 后 + 层序遍历(听说面试会考?)

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:数据结构
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


目录

  • 一、需要使用到的代码
      • 1.1 二叉树的基本实现
      • 1.2 栈
      • 1.3 队列
  • 二、非递归实现二叉树的前序遍历
      • 2.1 思路
      • 2.2 代码实现
  • 三、非递归实现二叉树的前序遍历
      • 3.1 思路
      • 3.2 代码实现
  • 四、后序遍历
      • 4.1 思路
      • 4.2 代码实现
  • 五、层序遍历
      • 5.1 思路
      • 5.2 代码实现
      • 5.3 整个测试结果
  • 六、总结

一、需要使用到的代码

1.1 二叉树的基本实现

二叉树的基本实现在以往博客已经详细讨论过了,这里直接给出本篇博客的所需用到的源代码。【数据结构】二叉树的链式结构(笔记总结)

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

typedef int DataType;

typedef struct BinaryTree
{
    DataType _data;
    struct BinaryTree *_left;
    struct BinaryTree *_right;
} BinaryTree;

// 创建结点
BinaryTree *CreateNode(DataType x)
{
    BinaryTree *newnode = (BinaryTree *)malloc(sizeof(BinaryTree));
    if (newnode == NULL)
    {
        printf("CreateNode failed\n");
        return NULL;
    }
    newnode->_left = NULL;
    newnode->_right = NULL;
    newnode->_data = x;

    return newnode;
}

// 建树
BinaryTree *CreateTree()
{
    // 假定树的模型如下所示
    //    1
    //  2   3
    // 4  5    6
    BinaryTree *node1 = CreateNode(1);
    BinaryTree *node2 = CreateNode(2);
    BinaryTree *node3 = CreateNode(3);
    BinaryTree *node4 = CreateNode(4);
    BinaryTree *node5 = CreateNode(5);
    BinaryTree *node6 = CreateNode(6);

    node1->_left = node2;
    node1->_right = node3;
    node2->_left = node4;
    node2->_right = node5;
    node3->_right = node6;

    return node1;
}

// 递归实现前序遍历
void PreOrder(BinaryTree *root)
{
    if (root == NULL)
        return;

    printf("%d ", root->_data);
    PreOrder(root->_left);
    PreOrder(root->_right);
}

// 递归实现中序遍历
void InOrder(BinaryTree *root)
{
    if (root == NULL)
        return;

    InOrder(root->_left);
    printf("%d ", root->_data);
    InOrder(root->_right);
}

// 递归实现后序遍历
void PostOrder(BinaryTree *root)
{
    if (root == NULL)
        return;

    PostOrder(root->_left);
    PostOrder(root->_right);
    printf("%d ", root->_data);
}

在以上源代码中,我另外给出了递归实现遍历的版本,目的是为了和非递归(迭代)版进行对比。

1.2 栈

// 需要存储的数据类型是二叉树结构体的指针!
typedef BinaryTree *DataType1;

typedef struct stack
{
    DataType1 *_a;
    int size;
    int capacity;
} stack;

void StackInit(stack *st)
{
    st->_a = (DataType1 *)malloc(sizeof(DataType1) * 4); // 假设默认大小为4
    if (st->_a == NULL)
    {
        printf("st->_a malloc failed\n");
        return;
    }
    st->capacity = 4;
    st->size = 0;
}

// 入栈
void PushStack(stack *st, DataType1 val)
{
    if (st->capacity == st->size)
    {
    	// 每次扩大两倍
        DataType1 *newcapacity = (DataType1 *)realloc(st->_a, sizeof(DataType1) * 4 * 2); 
        if (newcapacity == NULL)
        {
            printf("st->_a realloc failed\n");
            return;
        }
        st->_a = newcapacity;
        st->capacity *= 2;
    }

    st->_a[st->size] = val;
    st->size++;
}

// 判断栈是否为空
bool StackEmpty(stack *st)
{
    return st->size == 0;
}

// 出栈
void PopStack(stack *st)
{
    if (StackEmpty(st))
    {
        printf("stack is empty\n");
        return;
    }
    st->size--;
}

// 访问栈顶元素
DataType1 StackTop(stack *st)
{
     return st->_a[st->size - 1];
}

栈是后面前、中、后序遍历所需要的。但是需要注意的是:栈需要存储的数据类型是二叉树结构体的指针。为什么?在后面会详细说明。

1.3 队列

// 需要存储的数据类型是二叉树结构体的指针
typedef BinaryTree *QueueType;
typedef struct QueueNode
{
    QueueType _val;
    struct QueueNode *_next;
} QueueNode;

typedef struct Queue
{
    QueueNode *tail;
    QueueNode *head;
} Queue;

// 初始化队列
void InitQueue(Queue *q)
{
    q->tail = q->head = NULL;
}

// 插入元素
void PushQueue(Queue *q, QueueType x)
{
    QueueNode *newnode = (QueueNode *)malloc(sizeof(QueueNode));
    if (newnode == NULL)
    {
        printf("newnode create failed\n");
        return;
    }
    newnode->_next = NULL;
    newnode->_val = x;

    if (q->head == NULL)
    {
        if (q->tail != NULL)
            return;
        q->head = q->tail = newnode;
    }
    else
    {
        q->tail->_next = newnode;
        q->tail = newnode;
    }
}

// 判断队列是否为空
bool QueueEmpty(Queue *q)
{
    return (q->head == NULL) && (q->tail == NULL);
}

// 队头元素
QueueType FrontQueue(Queue *q)
{
    return q->head->_val;
}

// 出队列
void PopQueue(Queue *q)
{
    if (QueueEmpty(q))
    {
        printf("Queue is empty\n");
        return;
    }

    if (q->head->_next == NULL)
    {
        free(q->head);
        q->head = q->tail = NULL;
    }
    else
    {
        QueueNode *next = q->head->_next;
        free(q->head);
        q->head = next;
    }
}

队列是为层序遍历所准备的同理地,队列存储的数据类型同样也要是二叉树结构体指针。

为了快速实现二叉树的遍历,以上栈和队列的细节代码并不完整。详细的可以参考往期博客:点击跳转

话不多说,现在进入正题!

二、非递归实现二叉树的前序遍历

2.1 思路

请看下图

在这里插入图片描述

最后回过头来讲讲为什么栈的存储的类型要是二叉树结构体的指针?

通过上图,我们总结了:结点出栈,需要带入其左右孩子。因此,如果不是其结构体指针,那么也就无法将root的左右孩子入栈了。注意:也不能存结构体。因为一个结构体太大了,而指针的大小只有4/8字节

2.2 代码实现

// 非递归实现前序遍历
void PreOrder_nonR(BinaryTree *root)
{
	// 1. 需要一个赋值栈
    stack st;
    StackInit(&st);
	
	// 2. 如果根结点不为空入栈
    if (root != NULL)
    {
        PushStack(&st, root);
    }
    
    while (!StackEmpty(&st))
    {
    	// 记录栈顶元素
        BinaryTree *top = StackTop(&st);
		// 3. 出栈后带入其左右孩子
        PopStack(&st);
        printf("%d ", top->_data);
        // !要注意顺序:先带右孩子,再带左孩子 
        if (top->_right)
            PushStack(&st, top->_right);

        if (top->_left)
            PushStack(&st, top->_left);
    }
}

三、非递归实现二叉树的前序遍历

3.1 思路

请看下图

在这里插入图片描述

3.2 代码实现

void InOrder_nonR(BinaryTree *root)
{
	// 1. 需要一个辅助栈
    stack st;
    StackInit(&st);
    
    // 如果一开始根结点为NULL
    // 直接返回
    if (root == 0)
        return;

    // 2.遍历左孩子,将其全部入栈
    BinaryTree *cur = root;
    while (cur)
    {
        PushStack(&st, cur);
        cur = cur->_left;
    }

    while (!StackEmpty(&st))
    {
    	// 出栈打印
        BinaryTree *top = StackTop(&st);
        PopStack(&st);
        printf("%d ", top->_data);

        // 特判:出栈结点存在右孩子
        if (top->_right)
        {
        	// 将其入栈
            PushStack(&st, top->_right);
            // 然后还要特殊判断这个右孩子有没有左孩子
            // 因为我们要保证 先左 再根 再右
            BinaryTree *cur2 = top->_right;
            while (cur2->_left)
            {
                PushStack(&st, cur2->_left);
                cur2 = cur2->_left;
            }
        }
    }
}

四、后序遍历

4.1 思路

后序遍历我就不画图了,本人一开始写非递归后序遍历写了好久,都失败了(太菜了)。直到我看到一个视频,才知道原来后序遍历这么简单!

首先可以参考前序遍历(根左右)。因此,我们只要将前序遍历的代码逻辑的遍历顺序左和右对调一下,就变成根右左,最后再对其逆序,就是左右根,也就是后序遍历的结果了

4.2 代码实现

void PostOrder_nonR(BinaryTree *root)
{
    int res[6]; // 为了逆序
    int i = 0; // 用于遍历res数组
    memset(res, 0, sizeof(int));

    stack st;
    StackInit(&st);

    if (root != NULL)
    {
        PushStack(&st, root);
    }
    while (!StackEmpty(&st))
    {
        BinaryTree *top = StackTop(&st);
        PopStack(&st);
        res[i++] = top->_data;
		
		// 将前序遍历的代码逻辑的遍历顺序对调
        if (top->_left)
            PushStack(&st, top->_left);
        if (top->_right)
            PushStack(&st, top->_right);
    }
	// 最后逆序输出即可
    for (int k = i - 1; k >= 0; k--)
    {
        printf("%d ", res[k]);
    }
    printf("\n");
}

五、层序遍历

5.1 思路

层序遍历顾名思义就是一层一层遍历,那么就不能使用栈,得使用队列。

步骤:使用一个队列,出一个结点,带入它的孩子结点

  • 如果树不为空,就先让根结点入队列
    在这里插入图片描述

  • 然后出队列(打印1),再把1的左孩子和右孩子带入队列
    在这里插入图片描述

  • 接着让2出队列,再把2的孩子入队列
    在这里插入图片描述

  • 同理,再让4出队列,把它的孩子入队列
    在这里插入图片描述

  • 最后如果队列为空,即完成层序遍历
    在这里插入图片描述

5.2 代码实现

void LevelOrder(BinaryTree *root)
{
	// 1. 需要辅助队列
    Queue q;
    InitQueue(&q);
	
	// 如果一开始根结点root不为空
	// 则入队列
    if (root != NULL)
        PushQueue(&q, root);
	
	// 然后出双亲结点,带入子结点
    while (!QueueEmpty(&q))
    {
        BinaryTree *front = FrontQueue(&q);
        PopQueue(&q);

        printf("%d ", front->_data);
		
		// 带入子结点
        if (front->_left)
            PushQueue(&q, front->_left);

        if (front->_right)
            PushQueue(&q, front->_right);
    }
}

5.3 整个测试结果

在这里插入图片描述

六、总结

对于数据结构,还是得建议多画画图。最后我不将所有的代码整合到一块,读者只需理解,最好自己实现一遍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/140685.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在OpenWrt上部署uhttpd搭建web服务器,并实现公网远程访问

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

数据分析 - 思考题

上班路上刷到的有趣题

BEVFusion简介、环境配置与安装以及遇到的各种报错处理

BEVFusion简介、环境配置与安装以及遇到的各种报错处理 BEVFusion简介BEVFusion环境配置与安装报错解决 BEVFusion简介 针对点云投射到图像的多模态融合和图像投射到点云的多模态融合&#xff0c;前者会损失空间几何信息&#xff0c;后者会损失图像语义信息&#xff0c;这两种…

DMDEM部署说明-详细步骤-(DM8达梦数据库)

DMDEM部署说明-详细步骤-DM8达梦数据库 环境介绍1 部署DM8 数据库1.1 创建一个数据库作为DEM后台数据库1.2 创建数据库用户 DEM1.3 使用DEM用户导入dem_init.sql 2 配置tomcat2.1 配置/tomcat/conf/server.xml2.2 修改jvm启动参数 3 配置JAVA 1.8及以上版本的运行时环境3.1 配置…

【狂神说Java】SpringCloud | Netflix | Eureka | Ribbon | Feign | Zull | config | 详细笔记(全)

✅作者简介&#xff1a;CSDN内容合伙人、信息安全专业在校大学生&#x1f3c6; &#x1f525;系列专栏 &#xff1a;狂神说Java &#x1f4c3;新人博主 &#xff1a;欢迎点赞收藏关注&#xff0c;会回访&#xff01; &#x1f4ac;舞台再大&#xff0c;你不上台&#xff0c;永远…

基于连续Hopfield神经网络优化——旅行商问题优化计算

大家好&#xff0c;我是带我去滑雪&#xff01; 利用神经网络解决组合优化问题是神经网络应用的一个重要方面。所谓组合优化问题&#xff0c;就是在给定约束条件下&#xff0c;使目标函数极小&#xff08;或极大&#xff09;的变量组合问题。将Hopfield网络应用于求解组合优化问…

c++四种类型转换

首先我们要先引入上行转换和下行转换的概念 所谓上行转换&#xff0c;即将原来的子类指针转换成父类指针&#xff1b; 下行转换即将原来的父类指针转换成子类指针 由于子类对象的空间较大&#xff0c;所以把子类强制转换父类给父类指针赋值时&#xff0c;父类指针对象能读取…

Android图形系统之X11、Weston、Wayland、Mesa3D、ANGLE、SwiftShader介绍(十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

力扣刷题-二叉树-翻转二叉树

226.翻转二叉树 翻转一棵二叉树。 思路 参考&#xff1a; https://www.programmercarl.com/0226.%E7%BF%BB%E8%BD%AC%E4%BA%8C%E5%8F%89%E6%A0%91.html#%E6%80%9D%E8%B7%AF 如果要从整个树来看&#xff0c;翻转还真的挺复杂&#xff0c;整个树以中间分割线进行翻转&#xf…

基于springboot+vue健身管理系统

基于springbootvue健身管理系统 摘要 健身管理系统是一款基于Spring Boot和Vue.js的全栈应用&#xff0c;致力于为用户提供全面、个性化的健身管理体验。通过Spring Boot构建的后端&#xff0c;系统提供了强大的RESTful API支持&#xff0c;包括用户管理、健身计划制定和健康数…

深度学习_12_softmax_图片识别优化版代码

因为图片识别很多代码都包装在d2l库里了&#xff0c;直接调用就行了 完整代码&#xff1a; import torch from torch import nn from d2l import torch as d2l"获取训练集&获取检测集" batch_size 256 train_iter, test_iter d2l.load_data_fashion_mnist(ba…

msvcp71.dll,msvcr71.dll丢失的最简单的解决方法

在计算机使用过程中&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是MSVCR71.dll缺失。这个问题可能会导致某些应用程序无法正常运行&#xff0c;给用户带来困扰。本文将介绍5个修复MSVCR71.dll缺失的方案&#xff0c;帮助用户解决这一问题。 一、重新安装相关…

2756基于微信小程序的图书商城系统

摘要 本文将详细介绍基于微信小程序的图书商城系统的设计和实现。该系统包括服务器端和客户端两部分&#xff0c;能够满足管理员和普通用户的需求。通过对用户需求和功能的分析&#xff0c;本文将详细阐述系统设计的关键环节&#xff0c;包括数据库设计和界面设计。最后&#…

安全框架SpringSecurity-2(集成thymeleaf集成验证码JWT)

一、SpringSecurity 集成thymeleaf ①&#xff1a;复制并修改工程 复制04_spring_security并重命名为05_spring_security_thymeleaf ②&#xff1a;添加配置和依赖 添加thymeleaf依赖 <dependency><groupId>org.springframework.boot</groupId><artif…

C++--二叉树经典例题

本文&#xff0c;我们主要讲解一些适合用C的数据结构来求解的二叉树问题&#xff0c;其中涉及了二叉树的遍历&#xff0c;栈和队列等数据结构&#xff0c;递归与回溯等知识&#xff0c;希望可以帮助你进一步理解二叉树。 目录​​​​​​​ 1.二叉树的层序遍历 2.二叉树的公…

【STM32 CAN】STM32G47x 单片机FDCAN作为普通CAN外设使用教程

STM32G47x 单片机FDCAN作为普通CAN外设使用教程 控制器局域网总线&#xff08;CAN&#xff0c;Controller Area Network&#xff09;是一种用于实时应用的串行通讯协议总线&#xff0c;它可以使用双绞线来传输信号&#xff0c;是世界上应用最广泛的现场总线之一。CAN协议用于汽…

Swift爬虫程序

以下是一个简单的Swift爬虫程序&#xff0c;用于从前程无忧深圳地区招聘财务、会计的数据爬取数据&#xff1a; import Foundation import SwiftSoup// 创建一个请求对象&#xff0c;指定代理信息 var request URLRequest(url: URL(string: "https://www.51job.com/zh/c…

【Redis】Zset有序集合

上一篇&#xff1a; Hash哈希类型 https://blog.csdn.net/m0_67930426/article/details/134382507?spm1001.2014.3001.5502 目录 Zadd Zrange Zcard Zcount Zrem set是一个无序且元素不可重复的集合 而Zset是一个有序的集合,集合里的每个元素都有一个评分&#xff08;…

性能爆炸!Python多进程模式实现多核CPU并行计算

文章目录 前言一、.Python中的多进程模式二、提高程序执行效率的方法1.多进程并发执行任务2.进程池 3.消息队列4.共享内存5.异步IO 总结关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具…

深度学习之基于Pytorch服装图像分类识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介系统组成1. 数据集准备2. 数据预处理3. 模型构建4. 模型训练5. 模型评估 PyTorch的优势 二、功能三、系统四. 总结 一项目简介 深度学习在计算机视觉领域的…