【数据结构】之二叉树

二叉树是我们在数据结构中学到的第一个非线性结构,是后续学习更为复杂的树、图结构的基础。本文整理了二叉树的概念定义、基本操作、遍历算法、伪代码与代码实现以及实例说明,方便大家随时查找对应。

一、定义与基本术语

二叉树是一种树形结构,每个节点最多有两个子节点,分别称为左节点右节点

基本术语:

  • 根节点:树的顶部节点。
  • 叶节点:没有子节点的节点。
  • 父节点:某个节点的直接上级节点。
  • 子节点:某个节点的直接下级节点。
  • 兄弟节点:具有相同父节点的节点。
  • 深度:从根节点到某个节点的边的数量。
  • 高度:从某个节点到最远叶节点的边的数量。

        A (Depth: 0, Root)
       /    
     B (Depth: 1)
    /   \
 C1   C2(Depth: 2)
   \
    D (Depth: 3, Right)
     \
      E (Depth: 4, Right)
     /  \
    F1   F2(Depth: 4) 

二、主要特点

  1. 递归性质:二叉树的许多操作可以通过递归实现。
  2. 动态集合:二叉树可以用来实现动态集合,支持插入、删除和查找操作。
  3. 层次结构:二叉树具有明确的层次结构,适合表示具有层次关系的数据。

三、基本操作

1. 构建二叉树:

① 构建节点:

struct TreeNode{int key;TreeNode* left;TreeNode* right;TreeNode(int k): key(k),left(nullptr),right(nullptr){}
};

② 插入节点:

插入操作会根据节点的值来决定插入的位置。例如,在二叉搜索树(BST)中,插入操作遵循以下规则:

  • 如果当前节点的值大于插入值,则递归地插入到左子树。
  • 如果当前节点的值小于插入值,则递归地插入到右子树。
  • 如果当前节点的值等于插入值,则可以选择不插入或插入到左/右子树。
TreeNode* insert(TreeNode* node, int key){if(node == nullptr){return new TreeNode(key);}if(key < node->key){node->left = insert(node->left, key);}else if(key > node->key){node->right = insert(node->right,key);}return node;
}

③ 构建树:

从一个空树开始,通过多次插入节点,可以逐步构建二叉树。

TreeNode* root = nullptr;
// 如果是基于一个给定数组构建二叉树
for(int i = 0;i<length;i++){root = insert(root,nums[i]);
}
//手动输入构建
root = insert(root,10);
root = insert(root,4);
root = insert(root, 7);
root = insert(root, 20);
root = insert(root, 34);
root = insert(root, 16);
root = insert(root, 8);

 2. 删除节点:

TreeNode* deleteNode(TreeNode* node, int key){if(node == nullptr) return node;if(key < node->key){node->left = deleteNode(node->left, key);} else if (key > node->key) {node->right = deleteNode(node->right, key);}else{if(node->left == nullptr){TreeNode* tmp = node->right;delete node;return tmp;}else if(node->right == nullptr){TreeNode* tmp = node->left;delete node;return tmp;}TreeNode* temp = findMin(node->right);node->key = temp->key;node->right = deleteNode(node->right, temp->key);}return node;
}TreeNode* findMin(TreeNode* node){while(node->left != nullptr){node = node->left;}return node;
}

3. 查找节点:

TreeNode* searchNode(TreeNode* node, int key){if(node == nullptr||node->key = key) return node;if(key < node->key){return searchNode(node->left,key);}if(key > node->key){return searchNode(node->right, key);}
}

四、遍历算法

需要了解:前序遍历、中序遍历、后序遍历、层序遍历

1. 前序遍历:

        A
       / \
      B   C
     / \   \
    D   E   F

遍历顺序:A->B->D->E->C->F

        1
       / \
      2   3
     / \   \
    4   5   6
   / \
  7   8

遍历顺序:1->2->4->7->8->5->3->6

前序遍历的访问顺序:根节点-->左子树-->右子树

// 以int整数型为例
vector<int> result
void preorder(TreeNode* node){if(node == nullptr){return;}// 如果题目要求的是前序遍历打印节点值,那么可以直接:// cout<<node->key<<" ";// 如果题目要求前序遍历将节点值存储到数组中,需要先定义一个vector数组(如↑),再进行存储result.push_back(node_key);preorder(node->left);preorder(nofe->right);
}

 2. 中序遍历

        A
       / \
      B   C
     / \   \
    D   E   F

遍历顺序:D->B->E->A->C->F

        1
       / \
      2   3
     / \   \
    4   5   6
   / \
  7   8

遍历顺序:7->4->8->2->5->1->3->6 

        10
       /    \
      6    14
     /  \    /    \
    4   8 12  16

遍历顺序:4->6->8->10->12->14->16

中序遍历的访问顺序:左子树-->根节点-->右子树

vector<int> result;
void inorder(TreeNode* node) {if (node == nullptr) return;inorder(node->left);visit(node);inorder(node->right);
}void visit(TreeNode* node){// cout<< node->key <<" ";result.push_back(node->key);
}

3. 后序遍历

        A
       / \
      B   C
     / \     \
    D   E   F

遍历顺序:D->E->B->F->C->A

        1
       / \
      2   3
     /  \    \
    4   5   6
   / \
  7   8

遍历顺序:7->8->4->5->2->6->3->1 

后序遍历的访问顺序:左子树->右子树->根节点。

void postorder(TreeNode* node){if(node==nullptr) return;postorder(node->left);postorder(node->right);visit(node);
}
//visit函数要根据题目要求来定义,示例可参考中序遍历的定义

4. 层序遍历

        A
       / \
      B   C
     / \     \
    D   E   F

遍历顺序:A->B->C->D->E->F

        1
       / \
      2   3
     /    /  \
    4   5   6
        /
      7 

遍历顺序:1->2->3->4->5->6->7

层序遍历的顺序是:从上到下,从左到右

层序遍历的结果特别好写,从上往下盯齐就没问题,但是代码是最长的。。

先来看看伪代码:

LevelOrder(root)
    queue = new Queue()
    queue.enqueue(root)
    while not queue.isEmpty()
        node = queue.dequeue()
        visit(node)
        if node.left != null
            queue.enqueue(node.left)
        if node.right != null
            queue.enqueue(node.right)

void levelorder(TreeNode* root){if(root == nullptr) return;queue<TreeNode* > q; // 创建一个队列,用于存储待访问的节点q.push(root); // 把现在的根节点存进去while(!q.empty()){TreeNode* node = q.front(); // 取出队列的第一个节点q.pop(); // 将该节点从队列中移除visit(node);if (node->left != nullptr) q.push(node->left);if (node->right != nullptr) q.push(node->right); // 这里加入队列的顺序是先左再右,队列又是FIFO结构,遍历得到的顺序也就满足了先左再右}
}

五、适用场景举例

  1. 表达式树:用于表示和计算算术表达式。
  2. 二叉搜索树(BST):用于实现动态集合,支持高效的插入、删除和查找操作。
  3. 哈夫曼树:用于数据压缩。
  4. :用于实现优先队列。
  5. 线索二叉树:用于高效地遍历二叉树。

六、Leetcode 二叉树 练习题汇总

1. 初级题目

题号题目名称知识点
94二叉树的中序遍历中序遍历,递归与迭代
144二叉树的前序遍历前序遍历,递归与迭代
145二叉树的后序遍历后序遍历,递归与迭代
102二叉树的层序遍历层序遍历,队列的应用
226翻转二叉树递归,树的翻转
617合并二叉树递归,树的合并
589N叉树的前序遍历N叉树,前序遍历
897递增顺序搜索树二叉搜索树,中序遍历的应用

2. 中级题目 

题号题目名称知识点
98验证二叉搜索树二叉搜索树的性质,递归
113路径总和 II深度优先搜索,路径求和
257二叉树的所有路径深度优先搜索,路径记录
114二叉树展开为链表递归,树的展开
116填充每个节点的下一个右侧节点指针层序遍历,队列的应用
104二叉树的最大深度深度优先搜索或层序遍历
543二叉树的直径深度优先搜索,树的直径
236二叉树的最近公共祖先递归,最近公共祖先

 希望对你有帮助!

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

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

相关文章

Honeyview:快速浏览各类图像

Honeyview是一款免费、轻量级图片查看工具‌&#xff0c;专为快速浏览各类图像设计&#xff0c;支持Windows系统‌。其核心优势在于‌极速加载‌与‌广泛格式兼容性‌&#xff0c;可替代系统自带的图片查看工具&#xff0c;尤其适合需要处理专业图像&#xff08;如PSD、RAW&…

Streamlit性能优化:缓存与状态管理实战

目录 &#x1f4cc; 核心特性 &#x1f4cc; 运行原理 &#xff08;1&#xff09;全脚本执行 &#xff08;2&#xff09;差异更新 &#x1f4cc; 缓存机制 ❓为什么使用缓存&#xff1f; 使用st.cache_data的优化方案 缓存适用场景 使用st.session_state的优化方案 &…

十七、TCP编程

TCP 编程是网络通信的核心&#xff0c;其 API 围绕面向连接的特性设计&#xff0c;涵盖服务端和客户端的交互流程。以下是基于 ​C 语言的 TCP 编程核心 API 及使用流程的详细解析&#xff1a; 核心 API 概览 ​函数​角色​描述socket()通用创建套接字&#xff0c;指定协议族…

将外网下载的 Docker 镜像拷贝到内网运行

将外网下载的 Docker 镜像拷贝到内网运行&#xff0c;可以通过以下步骤实现&#xff1a; 一、在有外网访问权限的机器上操作 下载镜像 使用docker pull命令下载所需的镜像。例如&#xff0c;如果你需要下载一个名为nginx的镜像&#xff0c;可以运行以下命令&#xff1a;docke…

《深入理解生命周期与作用域:以C语言为例》

&#x1f680;个人主页&#xff1a;BabyZZの秘密日记 &#x1f4d6;收入专栏&#xff1a;C语言 &#x1f30d;文章目入 一、生命周期&#xff1a;变量的存在时间&#xff08;一&#xff09;生命周期的定义&#xff08;二&#xff09;C语言中的生命周期类型&#xff08;三&#…

Hqst的超薄千兆变压器HM82409S在Unitree宇树Go2智能机器狗的应用

本期拆解带来的是宇树科技推出的Go2智能机器狗&#xff0c;这款机器狗采用狗身体形态&#xff0c;前端设有激光雷达&#xff0c;摄像头和照明灯。在腿部设有12个铝合金精密关节电机&#xff0c;并配有足端力传感器&#xff0c;通过关节运动模拟狗的运动&#xff0c;并可做出多种…

壹起航:15年深耕,引领中国工厂出海新征程

在全球化浪潮汹涌澎湃的当下&#xff0c;中国工厂正以前所未有的热情和决心&#xff0c;将目光投向广阔的海外市场。然而&#xff0c;出海之路并非一帆风顺&#xff0c;建立品牌、获取稳定询盘、降低营销成本等难题&#xff0c;如同横亘在企业面前的高山&#xff0c;阻碍着他们…

【差分隐私相关概念】基础合成定理和高级合成技术简单关系

差分隐私中的合成定理用于分析多个机制组合时的隐私损失。基础合成定理和高级合成技术分别在不同场景下提供了隐私预算增长的估计&#xff0c;其关系如下&#xff1a; 基础合成定理&#xff08;线性增长&#xff09; 机制组合&#xff1a;当k个满足(ε, δ)-DP的机制按顺序组…

【异常处理】Clion IDE中cmake时头文件找不到 头文件飘红

如图所示是我的clion项目目录 我自定义的data_structure.h和func_declaration.h在unit_test.c中无法检索到 cmakelists.txt配置文件如下所示&#xff1a; cmake_minimum_required(VERSION 3.30) project(noc C) #设置头文件的目录 include_directories(${CMAKE_SOURCE_DIR}/…

MOS的驱动电流怎么计算?

一、MOS 驱动电流的计算方法 MOS 管在开关时&#xff0c;驱动电路主要是给栅极充放电。栅极电流 不是用来维持电流&#xff0c;而是用来克服电容的充放电需求&#xff0c;尤其是总栅极电荷 Qg。 驱动电流估算公式如下&#xff1a; I_drive Qg f_sw&#xff08;Qg&#xff…

GGML源码逐行调试(下)

目录 前言1. 简述2. 预分配计算图内存2.1 创建图内存分配器2.2 构建最坏情况的计算图2.3 预留计算图内存 3. 分词4. 模型推理与生成4.1 模型推理4.2 采样 结语下载链接参考 前言 学习 UP 主 比飞鸟贵重的多_HKL 的 GGML源码逐行调试 视频&#xff0c;记录下个人学习笔记&#x…

1.5-APP的架构\微信小程序的架构

1.5-APP的架构\微信小程序的架构 APP的三种开发架构&#xff1a; 原生态APP类型 APP-开发架构-原生态-IDEA 演示&#xff1a;remusic项目源码 NP管理器&#xff1a; http://normalplayer.top/ HttpCanary&#xff1a;https://github.com/mingww64/HttpCanary-SSL-Magisk 安全影…

用css画一条弧线

ui里有一条弧线&#xff0c;现在用css实现 关键代码 border-bottom-left-radius: 100% 7px 两个参数分别代表横向和纵向的深度border-bottom-right-radius: 100% 7px

MSCKF及可观性总结

可观性 参考链接 真实VIO系统不能观的维度是4&#xff08;位置和yaw角&#xff09;&#xff0c;由于EKF的转移和观测Jacobian矩阵的线性化点不同、不可观方向噪声的存在&#xff0c;实际MSCKF不能观的维度变成了3&#xff0c;绕重力轴的旋转&#xff08;yaw角&#xff09;被错…

【Hotspot虚拟机创建对象的过程是什么样的?】

1. 类加载检查 触发条件&#xff1a;当遇到 new 指令时&#xff0c;JVM首先检查该指令的参数&#xff08;类符号引用&#xff09;是否已在常量池中。检查内容&#xff1a; 类是否已被加载、解析和初始化。若未加载&#xff0c;则触发类加载过程&#xff08;加载 → 验证 → 准…

南墙WAF非标端口防护实战解析——指定端口安全策略深度剖析

本文系统解析非标端口DDoS攻击防护难点&#xff0c;重点阐述南墙WAF在指定端口防御中的技术突破。通过某金融机构真实攻防案例&#xff0c;结合Gartner最新防御架构模型&#xff0c;揭示如何构建基于智能流量建模的精准防护体系&#xff0c;为金融、政务等关键领域提供可落地的…

Context的全面解析:在不同技术应用中的通用作用与差异

Context的全面解析&#xff1a;在不同技术应用中的通用作用与差异 引言&#xff1a; 在软件开发中&#xff0c;“Context”这个概念被广泛使用。它不仅限于某个特定的技术或编程语言&#xff0c;实际上&#xff0c;Context 作为一种抽象的设计模式&#xff0c;贯穿在许多开发领…

寻找峰值 --- 二分查找

目录 一&#xff1a;题目 二&#xff1a;算法原理 三&#xff1a;代码实现 一&#xff1a;题目 题目链接&#xff1a;162. 寻找峰值 - 力扣&#xff08;LeetCode&#xff09; 二&#xff1a;算法原理 三&#xff1a;代码实现 class Solution { public:int findPeakElemen…

基础算法训练7

目录 库存管理II 翻转对 合并K个升序链表 存在重复元素II 字符串相乘 字符串解码 在每个树行中找最大值 数据流的中位数 被包围的区域 为高尔夫比赛砍树 库存管理II LCR 159. 库存管理 III - 力扣&#xff08;LeetCode&#xff09; 解法一&#xff1a;先进行排序&a…

从单机版到超级APP:MCP如何解锁AI的超能力

MCP&#xff1a;AI界的“万能充电宝”——让AI从此告别“语言不通”的尴尬&#xff01; 开篇&#xff1a;AI咖啡馆的尴尬日常 想象一下这样的场景&#xff1a; 一位AI助手在咖啡馆里手忙脚乱——它想帮用户点杯咖啡&#xff0c;但需要先写代码调用天气API&#xff08;“今天下…