数据结构(5.4_2)——树和森林的遍历

news2024/9/22 7:23:18

 树的先根遍历(深度优先遍历) 

若树非空,先访问根结点,再依次对每棵子树进行先根遍历

树的先根遍历序列和这棵树相应二叉树的先序序列相同。  

伪代码:

//树的先根遍历
void PreOrder(TreeNode* R) {
    if (R != NULL) {
        visit(R);//访问根结点
        while(R还有下一个子树T)
            PreOrder(T)//先根遍历下一棵子树
    }
}

实例:

#include <stdio.h>

// 定义树节点的结构体(兄弟兄弟表示法)
typedef struct CSNode {
    int data;                   // 节点存储的数据
    struct CSNode* firstchild; // 指向第一个子节点的指针
    struct CSNode* nextsibling; // 指向右兄弟节点的指针
} CSNode, *CSTree;

// 创建一个新树节点
CSNode* createTreeNode(int value) {
    CSNode* newNode = (CSNode*)malloc(sizeof(CSNode));
    if (newNode == NULL) {
        fprintf(stderr, "Memory allocation failed.\n");
        exit(EXIT_FAILURE);
    }
    newNode->data = value;
    newNode->firstchild = NULL;
    newNode->nextsibling = NULL;
    return newNode;
}

// 向树节点添加子节点
void addChild(CSTree parent, CSTree child) {
    if (parent == NULL || child == NULL) {
        return;
    }
    if (parent->firstchild == NULL) {
        parent->firstchild = child;
    } else {
        CSTree current = parent->firstchild;
        while (current->nextsibling != NULL) {
            current = current->nextsibling;
        }
        current->nextsibling = child;
    }
}

// 访问节点
void visit(CSNode* node) {
    if (node != NULL) {
        printf("%d ", node->data);
    }
}

// 先根遍历
void PreOrder(CSTree R) {
    if (R != NULL) {
        visit(R); // 访问根节点
        CSTree current = R->firstchild;
        while (current != NULL) {
            PreOrder(current); // 先根遍历子树
            current = current->nextsibling; // 移动到下一个兄弟节点
        }
    }
}

// 释放树的内存
void freeTree(CSTree root) {
    if (root == NULL) {
        return;
    }
    CSTree current = root->firstchild;
    while (current != NULL) {
        CSTree temp = current;
        current = current->nextsibling;
        freeTree(temp);
    }
    free(root);
}

// 主函数示例
int main() {
    // 创建树节点
    CSTree root = createTreeNode(1);
    CSTree child1 = createTreeNode(2);
    CSTree child2 = createTreeNode(3);
    CSTree child3 = createTreeNode(4);
    CSTree child4 = createTreeNode(5);
    CSTree child5 = createTreeNode(6);

    // 构建树结构
    addChild(root, child1);
    addChild(root, child2);
    addChild(child1, child3);
    addChild(child1, child4);
    addChild(child2, child5);

    // 执行先根遍历
    printf("Pre-order traversal of the tree is: ");
    PreOrder(root);
    printf("\n");

    // 释放树占用的内存
    freeTree(root);

    return 0;
}

 我们首先创建了一个树,然后通过 addChild 函数将子节点添加到其父节点。PreOrder 函数按照先根遍历的顺序访问每个节点。最后,我们通过 freeTree 函数释放了整个树所占用的内存。在主函数 main 中,我们构建了一个具体的树结构,并执行了先根遍历。

树的后根遍历 (深度优先遍历)

若树非空,先依次对每棵子树进行后根遍历,最后再访问根结点

树的后根遍历序列和这棵树相应二叉树的中序序列相同。   

伪代码:

//树的后根遍历
void PostOrder(TreeNode* R) {
    if (R != NULL) {
        while (R还有下一个子树T)
            PostOrder(T)//后根遍历下一棵子树
        visit(R);//访问根结点    
    }
}

实例:

#include <stdio.h>

// 定义树节点的结构体(兄弟兄弟表示法)
typedef struct CSNode {
    int data;                   // 节点存储的数据
    struct CSNode* firstchild; // 指向第一个子节点的指针
    struct CSNode* nextsibling; // 指向右兄弟节点的指针
} CSNode, *CSTree;

// 创建一个新树节点
CSNode* createTreeNode(int value) {
    CSNode* newNode = (CSNode*)malloc(sizeof(CSNode));
    if (newNode == NULL) {
        fprintf(stderr, "Memory allocation failed.\n");
        exit(EXIT_FAILURE);
    }
    newNode->data = value;
    newNode->firstchild = NULL;
    newNode->nextsibling = NULL;
    return newNode;
}

// 向树节点添加子节点
void addChild(CSTree parent, CSTree child) {
    if (parent == NULL || child == NULL) {
        return;
    }
    if (parent->firstchild == NULL) {
        parent->firstchild = child;
    } else {
        CSTree current = parent->firstchild;
        while (current->nextsibling != NULL) {
            current = current->nextsibling;
        }
        current->nextsibling = child;
    }
}

// 访问节点
void visit(CSNode* node) {
    if (node != NULL) {
        printf("%d ", node->data);
    }
}

// 后根遍历
void PostOrder(CSTree R) {
    if (R != NULL) {
        CSTree current = R->firstchild;
        while (current != NULL) {
            PostOrder(current); // 后根遍历子树
            current = current->nextsibling; // 移动到下一个兄弟节点
        }
        visit(R); // 访问根节点
    }
}

// 释放树的内存
void freeTree(CSTree root) {
    if (root == NULL) {
        return;
    }
    CSTree current = root->firstchild;
    while (current != NULL) {
        CSTree temp = current;
        current = current->nextsibling;
        freeTree(temp);
    }
    free(root);
}

// 主函数示例
int main() {
    // 创建树节点
    CSTree root = createTreeNode(1);
    CSTree child1 = createTreeNode(2);
    CSTree child2 = createTreeNode(3);
    CSTree child3 = createTreeNode(4);
    CSTree child4 = createTreeNode(5);
    CSTree child5 = createTreeNode(6);

    // 构建树结构
    addChild(root, child1);
    addChild(root, child2);
    addChild(child1, child3);
    addChild(child1, child4);
    addChild(child2, child5);

    // 执行后根遍历
    printf("Post-order traversal of the tree is: ");
    PostOrder(root);
    printf("\n");

    // 释放树占用的内存
    freeTree(root);

    return 0;
}

我们首先创建了一个树,然后通过 addChild 函数将子节点添加到其父节点。PostOrder 函数按照后根遍历的顺序访问每个节点。首先递归地遍历所有子树,然后访问根节点。最后,我们通过 freeTree 函数释放了整个树所占用的内存。在主函数 main 中,我们构建了一个具体的树结构,并执行了后根遍历。

树的层次遍历 (广度优先遍历)

用队列实现:

  1. 若树非空,则根节点入队
  2. 若队列非空,队头元素出队并访问,同时将该元素的孩子依次入队
  3. 重复2直到队列为空

伪代码:

// 树的层次遍历
void LevelOrder(TreeNode* root) {
    if (root == NULL) {
        return;
    }
    
    // 创建一个队列,用于存储待访问的节点
    Queue queue;
    initializeQueue(&queue);
    
    // 将根节点入队
    enqueue(queue, root);
    
    // 当队列不为空时,继续遍历
    while (!isEmpty(queue)) {
        // 出队一个节点
        TreeNode* current = dequeue(queue);
        
        // 访问当前节点
        visit(current);
        
        // 获取当前节点的所有子节点
        TreeNode* child = current->firstchild;
        
        // 遍历当前节点的所有子节点
        while (child != NULL) {
            enqueue(queue, child);
            child = child->nextsibling;
        }
    }
}

// 辅助函数:初始化队列
void initializeQueue(Queue* queue) {
    queue->front = NULL;
    queue->rear = NULL;
}

// 辅助函数:队列是否为空
bool isEmpty(Queue queue) {
    return queue.front == NULL;
}

// 辅助函数:将节点入队
void enqueue(Queue* queue, TreeNode* node) {
    QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
    if (newNode == NULL) {
        // 处理内存分配失败的情况
    }
    newNode->node = node;
    newNode->next = NULL;
    
    if (queue->rear == NULL) {
        queue->front = newNode;
    } else {
        queue->rear->next = newNode;
    }
    queue->rear = newNode;
}

// 辅助函数:从队列中出队一个节点
TreeNode* dequeue(Queue* queue) {
    if (isEmpty(*queue)) {
        return NULL;
    }
    
    TreeNode* frontNode = queue->front->node;
    QueueNode* temp = queue->front;
    queue->front = queue->front->next;
    
    if (queue->front == NULL) {
        queue->rear = NULL;
    }
    
    free(temp);
    return frontNode;
}

 代码实例:

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

// 定义树节点的结构体(兄弟兄弟表示法)
typedef struct CSNode {
    int data;                   // 节点存储的数据
    struct CSNode* firstchild; // 指向第一个子节点的指针
    struct CSNode* nextsibling; // 指向右兄弟节点的指针
} CSNode, *CSTree;

// 创建一个新树节点
CSNode* createTreeNode(int value) {
    CSNode* newNode = (CSNode*)malloc(sizeof(CSNode));
    if (newNode == NULL) {
        fprintf(stderr, "Memory allocation failed.\n");
        exit(EXIT_FAILURE);
    }
    newNode->data = value;
    newNode->firstchild = NULL;
    newNode->nextsibling = NULL;
    return newNode;
}

// 向树节点添加子节点
void addChild(CSTree parent, CSTree child) {
    if (parent == NULL || child == NULL) {
        return;
    }
    if (parent->firstchild == NULL) {
        parent->firstchild = child;
    } else {
        CSTree current = parent->firstchild;
        while (current->nextsibling != NULL) {
            current = current->nextsibling;
        }
        current->nextsibling = child;
    }
}

// 访问节点
void visit(CSNode* node) {
    if (node != NULL) {
        printf("%d ", node->data);
    }
}

// 层次遍历
void LevelOrder(CSTree root) {
    if (root == NULL) {
        return;
    }
    CSTree current = root;
    bool isRoot = true;
    while (current != NULL) {
        if (isRoot) {
            visit(current); // 访问根节点
            isRoot = false;
        }
        CSTree temp = current->firstchild;
        while (temp != NULL) {
            visit(temp); // 访问当前节点的所有子节点
            temp = temp->nextsibling;
        }
        current = current->nextsibling; // 移动到下一个兄弟节点
    }
}

// 释放树的内存
void freeTree(CSTree root) {
    if (root == NULL) {
        return;
    }
    CSTree current = root->firstchild;
    while (current != NULL) {
        CSTree temp = current;
        current = current->nextsibling;
        freeTree(temp);
    }
    free(root);
}

// 主函数示例
int main() {
    // 创建树节点
    CSTree root = createTreeNode(1);
    CSTree child1 = createTreeNode(2);
    CSTree child2 = createTreeNode(3);
    CSTree child3 = createTreeNode(4);
    CSTree child4 = createTreeNode(5);
    CSTree child5 = createTreeNode(6);

    // 构建树结构
    addChild(root, child1);
    addChild(root, child2);
    addChild(child1, child3);
    addChild(child1, child4);
    addChild(child2, child5);

    // 执行层次遍历
    printf("Level-order traversal of the tree is: ");
    LevelOrder(root);
    printf("\n");

    // 释放树占用的内存
    freeTree(root);

    return 0;
}

我们首先创建了一个树,然后通过 addChild 函数将子节点添加到其父节点。LevelOrder 函数按照层次遍历的顺序访问每个节点。首先将根节点入队,然后在循环中不断从队列中出队节点并访问它,然后将它的所有子节点入队。当队列为空时,遍历结束。最后,我们通过 freeTree 函数释放了整个树所占用的内存。在主函数 main 中,我们构建了一个具体的树结构,并执行了层次遍历。 

 森林。森林是m(m>=0)棵互不相交的树的集合。每棵树去掉根节点后,其各个子树又组成森林。

森林的先序遍历

若森林非空,则按如下规则进行遍历:

  1. 访问森林中第一棵树的根结点
  2. 先序遍历第一棵树中根结点的子树森林
  3. 先序遍历除去第一棵树之后剩余的树构成的森林

森林的先序遍历等于依次对各个树进行先根遍历 

方法二:将森林先转换成二叉树进行先序遍历

森林的中序遍历 

若森林非空,则按如下规则进行遍历:

  1. 中序遍历第一棵树中根结点的子树森林
  2. 访问森林中第一棵树的根结点
  3. 中序遍历除去第一棵树之后剩余的树构成的森林

森林的中序遍历等于依次对各个树进行后根遍历  

方法二:将森林先转换成二叉树进行中序遍历

总结:

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【WRF安装第四期(Ubuntu)】搭建WRF编译所需系统-WRF和WPS模型的安装

WRF安装第四期&#xff1a;搭建WRF编译所需系统-WRF和WPS模型的安装 1 WRF的编译安装&#xff08;Building WRF&#xff09;1.1 进入Build_WRF文件夹1.2 下载WRFV4.01.3 解压WRF安装包1.4 安装WRF选择#1&#xff1a;34选择#2&#xff1a;32 1.5 检查WRF是否安装成功1.5.1 WRF安…

ai文案生成器,文案自动生成好简单

随着科技的不断进步&#xff0c;AI在各个领域中扮演着越来越重要的角色。其中&#xff0c;ai文案生成器的出现给广告和市场营销行业带来了一场革命。曾经需要耗费大量时间和精力的文案创作过程&#xff0c;如今可以通过ai文案生成器轻松自动完成。这一创新技术的出现&#xff0…

什么是药物临床试验?

药物临床试验是指在人体上进行的新药试验研究&#xff0c;旨在确定新药的疗效、安全性、药代动力学和药效学。临床试验不仅帮助确认药物是否对特定疾病或症状有效&#xff0c;还帮助识别和评估药物的副作用和风险。 药物临床试验&#xff08;Clinical Trial&#xff0c;CT&…

数据结构:带索引的双链表IDL

IDLindexed double list 如图&#xff0c;下方是一个双链表&#xff0c;上方是索引。索引储存为结构体数组&#xff0c;结构体内包括一个指针&#xff0c;和长度。 假设索引只有一个&#xff0c;这时&#xff0c;它应该指向双链表的中间&#xff0c;这样才能提高搜索效率。称…

深入探索可擦除可编程只读存储器(EPROM)DS2502P+TR 1K位只添加存储器

DS2502PT&R产品描述&#xff1a; DS2502PT&R 为1K位只添加存储器&#xff0c;用于识别并存储产品的相关信息。产品批号或特殊的产品信息可以通过最少的接口访问—例如&#xff0c;微控制器的一个端口引脚。DS2502PT&R 具有一个工厂光刻注册码&#xff0c;其中包括…

UE5 大鹅的点击移动 第三人称

文章目录 一、创建动画蓝图二、创建 Location 地标三、Character 和 PlayerControl 的控制四、实现效果 一、创建动画蓝图 这里以 UE5 从零开始制作跟随的大鹅-CSDN博客 创建的动态资产创建动画蓝图&#xff1b;需要用到的资产列表有&#xff1a;大鹅的骨骼网格体&#xff0c;…

【大模型从入门到精通4】openAI API 分类

这里写目录标题 分类理解 SYSTEM 和 USER 在 AI 对话中的角色System MessageUser Message工作原理示例分类示例更多分类示例理论问题理论 分类 理解 SYSTEM 和 USER 在 AI 对话中的角色 在分类任务中&#xff0c;通常需要向模型提供一个需要将其分类到预定义类别中的文本场景…

【数据结构与算法】堆顶删除

堆顶的删除 一.堆顶出列的原理二.堆顶出列的实现1.覆盖最大元素并出列2.向下调整成为堆 三.堆排序四,总结 一.堆顶出列的原理 还记得我们刚开始说的嘛,如果我想要拿出最大的,那么下一个最大的会花落谁家. 那么就需要用到堆顶出列的原理了. 然后我们再对顶节点,进行向下调整就可…

9-springCloud集成nacos config

本文介绍spring cloud集成nacos config的过程。 0、环境 jdk 1.8maven 3.8.1Idea 2021.1nacos 2.0.3 1、项目结构 根项目nacos-config-sample下有两个module&#xff0c;这两个module分别是两个springboot项目&#xff0c;都从nacos中获取连接mysql的连接参数。我们开工。 …

被遗忘的哑终端 —— 键盘键位演变的启发者

注&#xff1a;机翻&#xff0c;未校对。 The Forgotten World of Dumb Terminals 被遗忘的哑终端世界 A quick journey through the lost age of “glass teletypes.” 快速穿越失落的“玻璃电传打字机”时代。 From the earliest days of digital computers, researchers o…

【C++】-----继承(复杂的多继承及虚拟继承)

目录 前言 一、多继承 认识 继承顺序 二、菱形继承 三、菱形虚拟继承&#xff08;重难点&#xff09; 认识 底层原理&#xff08;细致&#xff09; 四、继承与组合 五、总结 前言 在前面我们所举的例子都是单继承&#xff0c;就是一个子类只有一个直接父类的关系&…

用Manim在图形和坐标轴上画线条

用Manim在图形和坐标轴上画线条 .画图像函数的切线 angle_of_tangent(x, graph, dx1e-08) angle_of_tangent(x, graph, dx1e-08)是 Manim 中用于计算图形在给定点的切线角度的函数。以下是对该函数参数的解释&#xff1a; 参数说明 x: 这是你想要计算切线角度的 x 坐标。在…

C++进阶之C++11

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言进阶 数据结构初阶 Linux C初阶 算法 C进阶 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂 目录 一.列表初始化 1.1一切皆可用列表初始化 1.2init…

代码随想录 day 29 贪心

第八章 贪心算法 part03 134. 加油站 本题有点难度&#xff0c;不太好想&#xff0c;推荐大家熟悉一下方法二 https://programmercarl.com/0134.%E5%8A%A0%E6%B2%B9%E7%AB%99.html 135. 分发糖果 本题涉及到一个思想&#xff0c;就是想处理好一边再处理另一边&#xff0c;不…

MySQL基础操作全攻略:增删改查实用指南(上)

本节目标&#xff1a; CRUD : Create, Retrieve &#xff0c; Update &#xff0c; Delete 新增数据 查询数据 修改数据 删除数据 1. CRUD 注释&#xff1a;在 SQL 中可以使用 “-- 空格 描述 ” 来表示注释说明 CRUD 即增加(Create)、查询(Retrieve)、更新(Update)、…

什么是 IDR —— Linux 内核中的一种整数管理机制

文章目录 1 什么是 IDR1.1 IDR 的设计目的 2 IDR 的结构和实现2.1 核心数据结构2.2 常用操作2.2.1 分配 ID2.2.2 查找指针2.2.3 删除映射 2.3 IDR 的优点 3 Linux 内核中的整数 ID3.1 作用3.2 常见的整数 ID 示例 4 为什么要将整数 ID 与指针关联4.1 举例说明4.2 好处4.3 示例代…

学习笔记-Cookie、Session、JWT

目录 一、验证码的生成与校验 1. 创建生成验证码的工具类 2. 写一个 Controller 3. 实现验证码验证 1. 获取验证码 2. 验证码请求过程 3. 验证码的校验 4. 原理说明 5. 验证 6. 总结 二、JWT登录鉴权 1. 为什么要做登录鉴权&#xff1f; 2. 什么是 JWT 3. JWT相比…

MATLAB优化模型(2)

一、前言 在MATLAB中实现动态规划、图论、网络流模型&#xff08;如最短路、最大流、最小生成树&#xff09;的优化模型&#xff0c;可以通过多种方法完成&#xff0c;但通常会依赖于MATLAB内置的函数或工具箱&#xff0c;比如Optimization Toolbox、Graph Theory Toolbox等。以…

Python 实现股票指标计算——SKDJ

SKDJ (Stochastic KDJ) - 慢速随机指标 1 公式 LOWV:LLV(LOW,N); HIGHV:HHV(HIGH,N); RSV:EMA((CLOSE-LOWV)/(HIGHV-LOWV)*100,M); K:EMA(RSV,M); D:MA(K,M); 2 数据准备 我们以科创50指数 000688 为例&#xff0c;指数开始日期为2019-12-31&#xff0c;数据格式如下&#…

Leetcode 第 135 场双周赛题解

Leetcode 第 135 场双周赛题解 Leetcode 第 135 场双周赛题解题目1&#xff1a;3222. 求出硬币游戏的赢家思路代码复杂度分析 题目2&#xff1a;3223. 操作后字符串的最短长度思路代码复杂度分析 题目3&#xff1a;3224. 使差值相等的最少数组改动次数思路代码复杂度分析 题目4…