【数据结构】树与二叉树(十):二叉树的先序遍历(非递归算法NPO)

2023-11-11 21:44

本文主要是介绍【数据结构】树与二叉树(十):二叉树的先序遍历(非递归算法NPO),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

5.2.1 二叉树

  二叉树是一种常见的树状数据结构,它由结点的有限集合组成。一个二叉树要么是空集,被称为空二叉树,要么由一个根结点和两棵不相交的子树组成,分别称为左子树右子树。每个结点最多有两个子结点,分别称为左子结点和右子结点。
在这里插入图片描述

二叉树性质

引理5.1:二叉树中层数为i的结点至多有 2 i 2^i 2i个,其中 i ≥ 0 i \geq 0 i0

引理5.2:高度为k的二叉树中至多有 2 k + 1 − 1 2^{k+1}-1 2k+11个结点,其中 k ≥ 0 k \geq 0 k0

引理5.3:设T是由n个结点构成的二叉树,其中叶结点个数为 n 0 n_0 n0,度数为2的结点个数为 n 2 n_2 n2,则有 n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1

  • 详细证明过程见前文:【数据结构】树与二叉树(三):二叉树的定义、特点、性质及相关证明

满二叉树、完全二叉树定义、特点及相关证明

  • 详细证明过程见前文:【数据结构】树与二叉树(四):满二叉树、完全二叉树及其性质

5.2.2 二叉树顺序存储

  二叉树的顺序存储是指将二叉树中所有结点按层次顺序存放在一块地址连续的存储空间中,详见:
【数据结构】树与二叉树(五):二叉树的顺序存储(初始化,插入结点,获取父节点、左右子节点等)

5.2.3 二叉树链接存储

  二叉树的链接存储系指二叉树诸结点被随机存放在内存空间中,结点之间的关系用指针说明。在链式存储中,每个二叉树结点都包含三个域:数据域(Data)、左指针域(Left)和右指针域(Right),用于存储结点的信息和指向子结点的指针,详见:
【数据结构】树与二叉树(六):二叉树的链式存储

5.2.4 二叉树的遍历

  • 遍历(Traversal)是对二叉树中所有节点按照一定顺序进行访问的过程。
  • 通过遍历,可以访问树中的每个节点,并按照特定的顺序对它们进行处理。
  • 对二叉树的一次完整遍历,可给出树中结点的一种线性排序。
    • 在二叉树中,常用的遍历方式有三种:先序遍历中序遍历后序遍历
    • 这三种遍历方式都可以递归地进行,它们的区别在于节点的访问顺序
      • 在实现遍历算法时,需要考虑递归终止条件和递归调用的顺序。
    • 还可以使用迭代的方式来实现遍历算法,使用栈或队列等数据结构来辅助实现。
  • 遍历是二叉树中基础而重要的操作,它为其他许多操作提供了基础,如搜索、插入、删除等。
    在这里插入图片描述

1-3 先序、中序、后序遍历递归实现及相关练习

【数据结构】树与二叉树(七):二叉树的遍历(先序、中序、后序及其C语言实现)

后序遍历递归实现

void postOrderTraversal(struct Node* root) {if (root == NULL) {return;}// 递归遍历左子树postOrderTraversal(root->left);// 递归遍历右子树postOrderTraversal(root->right);// 访问根节点printf("%c ", root->data);
}

4. 中序遍历非递归

【数据结构】树与二叉树(八):二叉树的中序遍历(非递归算法NIO)

5. 后序遍历非递归

【数据结构】树与二叉树(九):二叉树的后序遍历(非递归算法NPO)

6. 先序遍历非递归

a. 算法NPO

在这里插入图片描述
说明:该ADL语言算法流程为本人所写,不具备权威性,如有错误望忽视,请跳转至下文具体C语言实现部分。

b. 算法解读

  算法NPO(t)利用了一个辅助堆栈S来遍历二叉树T的所有节点。

  1. 如果根节点t为空,则直接返回。
  2. 创建一个空堆栈S,并将根节点t和初始标记0入栈(S <= (t, 0))。
  3. 进入循环,只要堆栈S非空,执行以下步骤:
    a. 从堆栈S中弹出栈顶元素,将其赋值给(p, i)。
    b. 如果标记i为0,则表示节点p还未处理,打印节点p的值,并将左子节点入栈(S <= (Left§, 0)),然后将标记置为1(S <= (p, 1))。
    c. 如果标记i为1,则表示节点p的左子树已处理完毕,将右子节点入栈(S <= (Right§, 0)),然后将标记置为2(S <= (p, 2))。
    d. 如果标记i为2,则表示节点p的左右子树都已处理完毕,将节点p从堆栈S中弹出(S.pop())。
  4. 跳转到步骤3,继续循环,直到堆栈S为空。

c. 复杂度分析

  设二叉树有n个结点。算法NPO中,每个结点的状态都是从0→1→2,每个状态都要经历1次入栈和1次出栈,即入栈和出栈各执行3n次,另外,每个结点都进行1次访问,即访问还要执行n次,因此,算法NPO的时间复杂度为O(n).

d.代码实现

void nonRecursiveInOrder(struct Node* root) {struct Node* stack[100];  // 辅助堆栈,用于模拟递归调用栈int top = -1;  // 栈顶指针struct Node* current = root;while (current != NULL || top != -1) {// 将当前结点的左子结点入栈while (current != NULL) {stack[++top] = current;current = current->left;}// 弹出栈顶结点,并访问current = stack[top--];printf("%c ", current->data);// 处理右子结点current = current->right;}
}

5. 代码整合

#include <stdio.h>
#include <stdlib.h>// 二叉树结点的定义
struct Node {char data;struct Node* left;struct Node* right;
};// 创建新结点
struct Node* createNode(char data) {struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));if (newNode == NULL) {printf("Memory allocation failed!\n");exit(1);}newNode->data = data;newNode->left = NULL;newNode->right = NULL;return newNode;
}// 先序遍历
void PreOrderTraversal(struct Node* root) {if (root == NULL) {return;}// 访问根节点printf("%c ", root->data);// 递归遍历左子树PreOrderTraversal(root->left);// 递归遍历右子树PreOrderTraversal(root->right);}// 非递归先序遍历
void nonRecursivePreOrder(struct Node* root) {if (root == NULL) {return;}struct Node* stack[100];  // 辅助堆栈,用于模拟递归调用栈int top = -1;  // 栈顶指针stack[++top] = root;while (top != -1) {struct Node* current = stack[top--];printf("%c ", current->data);if (current->right != NULL) {stack[++top] = current->right;}if (current->left != NULL) {stack[++top] = current->left;}}
}int main() {// 创建一棵二叉树struct Node* root = createNode('a');root->left = createNode('b');root->right = createNode('c');root->left->left = createNode('d');root->left->right = createNode('e');root->left->right->left = createNode('f');root->left->right->right = createNode('g');// 递归先序序遍历二叉树printf("Recursive Pre-order traversal: \n");PreOrderTraversal(root);printf("\n");// 非递归先序遍历二叉树printf("Non-recursivePre-order traversal: \n");nonRecursivePreOrder(root);printf("\n");return 0;
}

在这里插入图片描述

这篇关于【数据结构】树与二叉树(十):二叉树的先序遍历(非递归算法NPO)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/393013

相关文章

MySQL存储过程之循环遍历查询的结果集详解

《MySQL存储过程之循环遍历查询的结果集详解》:本文主要介绍MySQL存储过程之循环遍历查询的结果集,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言1. 表结构2. 存储过程3. 关于存储过程的SQL补充总结前言近来碰到这样一个问题:在生产上导入的数据发现

python进行while遍历的常见错误解析

《python进行while遍历的常见错误解析》在Python中选择合适的遍历方式需要综合考虑可读性、性能和具体需求,本文就来和大家讲解一下python中while遍历常见错误以及所有遍历方法的优缺点... 目录一、超出数组范围问题分析错误复现解决方法关键区别二、continue使用问题分析正确写法关键点三

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

mysql递归查询语法WITH RECURSIVE的使用

《mysql递归查询语法WITHRECURSIVE的使用》本文主要介绍了mysql递归查询语法WITHRECURSIVE的使用,WITHRECURSIVE用于执行递归查询,特别适合处理层级结构或递归... 目录基本语法结构:关键部分解析:递归查询的工作流程:示例:员工与经理的层级关系解释:示例:树形结构的数

Springboot实现推荐系统的协同过滤算法

《Springboot实现推荐系统的协同过滤算法》协同过滤算法是一种在推荐系统中广泛使用的算法,用于预测用户对物品(如商品、电影、音乐等)的偏好,从而实现个性化推荐,下面给大家介绍Springboot... 目录前言基本原理 算法分类 计算方法应用场景 代码实现 前言协同过滤算法(Collaborativ

Java遍历HashMap的6种常见方式

《Java遍历HashMap的6种常见方式》这篇文章主要给大家介绍了关于Java遍历HashMap的6种常见方式,方法包括使用keySet()、entrySet()、forEach()、迭代器以及分别... 目录1,使用 keySet() 遍历键,再通过键获取值2,使用 entrySet() 遍历键值对3,

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

C语言函数递归实际应用举例详解

《C语言函数递归实际应用举例详解》程序调用自身的编程技巧称为递归,递归做为一种算法在程序设计语言中广泛应用,:本文主要介绍C语言函数递归实际应用举例的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录前言一、递归的概念与思想二、递归的限制条件 三、递归的实际应用举例(一)求 n 的阶乘(二)顺序打印

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

C#数据结构之字符串(string)详解

《C#数据结构之字符串(string)详解》:本文主要介绍C#数据结构之字符串(string),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录转义字符序列字符串的创建字符串的声明null字符串与空字符串重复单字符字符串的构造字符串的属性和常用方法属性常用方法总结摘