二叉树的基本知识

news2024/9/28 21:30:07

(写给未来遗忘的自己)

1.二叉树的种类

1. 满二叉树:所有分支都有数(都填满)

2. 完全二叉树:除了最底层没填满外其他的都满了,而且最底层从左到右是存在数的位置是连续的

3.二叉搜索树:二叉搜索树是一个有序树

                   $ 左子树不空,则左子树上所有节点的值小于根节点

                   $右子树不空,则右子树所有节点的值大于根节点

                  $ 左右子树下面也符合上述的两条

4.平衡二叉搜索树:空树或者左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是二                                  叉搜索树

2.二叉树的存储方式: 

1.链式存储

链式存储以类似链表的方式存储。

代码如下:

#include <iostream>
#include <queue>

using namespace std;

// 定义二叉树的节点结构
struct TreeNode {
    int data;
    TreeNode* left;
    TreeNode* right;

    // 构造函数
    TreeNode(int val) : data(val), left(nullptr), right(nullptr) {}
};

// 插入节点的函数
TreeNode* insert(TreeNode* root, int val) {
    // 如果根节点为空,创建新的节点作为根节点
    if (root == nullptr) {
        return new TreeNode(val);
    }

    // 使用队列进行层次遍历,找到第一个可以插入的空位置
    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        TreeNode* node = q.front();
        q.pop();

        // 插入左子节点
        if (node->left == nullptr) {
            node->left = new TreeNode(val);
            break;
        }
        else {
            q.push(node->left);
        }

        // 插入右子节点
        if (node->right == nullptr) {
            node->right = new TreeNode(val);
            break;
        }
        else {
            q.push(node->right);
        }
    }
    return root;
}

int main() {
    TreeNode* root=nullptr;

    // 插入节点
    root = insert(root, 1);
    root = insert(root, 2);
    root = insert(root, 3);
    root = insert(root, 4);


    return 0;
}

2.顺序存储

二叉树的节点按照层次遍历的顺序存储在数组中。对于一个节点在数组中的位置,可以通过以下规则来访问其子节点和父节点:

  • 节点在数组中的索引为 i:
    • 左子节点的位置为 2*i + 1
    • 右子节点的位置为 2*i + 2
    • 父节点的位置为 (i - 1) / 2(对整数除法取整)
         1
       /   \
      2     3
     / \   / \
    4   5 6   7

在顺序存储中,这棵树表示为一个数组:

[1, 2, 3, 4, 5, 6, 7]
#include <iostream>
#include <vector>

class BinaryTree {
public:
    // 构造函数,接受一个数组来初始化树
    BinaryTree(const std::vector<int>& elements) : tree(elements) {}

    // 获取指定节点的左子节点
    int leftChild(int index) {
        int leftIndex = 2 * index + 1;
        if (leftIndex < tree.size()) {
            return tree[leftIndex];
        } else {
            throw std::out_of_range("No left child.");
        }
    }

    // 获取指定节点的右子节点
    int rightChild(int index) {
        int rightIndex = 2 * index + 2;
        if (rightIndex < tree.size()) {
            return tree[rightIndex];
        } else {
            throw std::out_of_range("No right child.");
        }
    }

    // 获取指定节点的父节点
    int parent(int index) {
        if (index == 0) {
            throw std::out_of_range("No parent for root node.");
        }
        int parentIndex = (index - 1) / 2;
        return tree[parentIndex];
    }

    // 打印树的内容
    void printTree() {
        for (int value : tree) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }

private:
    std::vector<int> tree;
};

int main() {
    // 创建一个树 [1, 2, 3, 4, 5, 6, 7]
    std::vector<int> elements = {1, 2, 3, 4, 5, 6, 7};
    BinaryTree bt(elements);

    // 打印树
    bt.printTree();

    // 获取节点和打印其子节点
    try {
        std::cout << "Left child of 0: " << bt.leftChild(0) << std::endl;
        std::cout << "Right child of 0: " << bt.rightChild(0) << std::endl;
        std::cout << "Parent of 1: " << bt.parent(1) << std::endl;
        std::cout << "Parent of 2: " << bt.parent(2) << std::endl;
    } catch (const std::out_of_range& e) {
        std::cout << e.what() << std::endl;
    }

    return 0;
}

优点:构造函数使用初始化列表直接初始化成员变量,避免额外的拷贝

缺点:不适合频繁变化的树结构(如节点插入和删除),因为需要移动大量节点。也不适用于不完全的二叉树,因为会浪费数组空间

3.二叉树的遍历方式:

               1
          /         \
        2            3
      /   \        /   \
    4      5     6     7
   / \    / \   / \   / \
  8   9 10  11 12 13 14 15
//三种方式都是深度优先算法,所以中(将中一直搜索到最底,没有了才开始下一个左)

//前序遍历:(中左右)

 1, 2, 4, 8, 9, 5, 10, 11, 3, 6, 12, 13, 7, 14, 15

//中序遍历:(左中右)

8, 4, 9, 2, 10, 5, 11, 1, 12, 6, 13, 3, 14, 7, 15

//后序遍历

 8, 9, 4, 10, 11, 5, 2, 12, 13, 6, 14, 15, 7, 3, 1

3.1递归遍历:

递归是一种简洁的实现方式,适合自然的分治问题。每次递归调用函数时,都会自动处理子树

前序遍历:(中左右)

#include <iostream>

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

// 递归前序遍历
void preorderTraversalRecursive(TreeNode* root) {
    if (root == nullptr) return;

    std::cout << root->val << " ";  // 访问根节点
    preorderTraversalRecursive(root->left);  // 遍历左子树
    preorderTraversalRecursive(root->right);  // 遍历右子树
}

中序遍历:(左中右)

void inorderTraversalRecursive(TreeNode* root) {
    if (root == nullptr) return;

    inorderTraversalRecursive(root->left);  // 遍历左子树
    std::cout << root->val << " ";  // 访问根节点
    inorderTraversalRecursive(root->right);  // 遍历右子树
}

后序遍历:(左右中) 

#include <iostream>

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

// 后序遍历的递归实现
void postorderTraversalRecursive(TreeNode* root) {
    if (root == nullptr) return;

    postorderTraversalRecursive(root->left);   // 递归遍历左子树
    postorderTraversalRecursive(root->right);  // 递归遍历右子树
    std::cout << root->val << " ";             // 访问根节点
}

3.2迭代遍历:

非递归遍历使用栈来显式地保存状态,可以避免函数调用栈的开销,并且更适合处理非常深的树结构,防止栈溢出。

前序遍历:

#include <iostream>
#include <stack>

void preorderTraversalIterative(TreeNode* root) {
    if (root == nullptr) return;

    std::stack<TreeNode*> s;
    s.push(root);

    while (!s.empty()) {
        TreeNode* node = s.top();
        s.pop();

        std::cout << node->val << " ";  // 访问根节点

        // 注意这里先压右子节点,再压左子节点,因为栈是后进先出
        if (node->right) s.push(node->right);
        if (node->left) s.push(node->left);
    }
}

中序遍历:

void inorderTraversalIterative(TreeNode* root) {
    std::stack<TreeNode*> s;
    TreeNode* curr = root;

    while (curr != nullptr || !s.empty()) {
        // 不断访问左子节点并压入栈中
        while (curr != nullptr) {
            s.push(curr);
            curr = curr->left;
        }

        // 弹出栈顶节点,访问该节点
        curr = s.top();
        s.pop();
        std::cout << curr->val << " ";

        // 遍历右子树
        curr = curr->right;
    }
}

后序遍历:

#include <iostream>
#include <stack>
#include <vector>

void postorderTraversalIterative(TreeNode* root) {
    if (root == nullptr) return;

    std::stack<TreeNode*> s1, s2;
    s1.push(root);

    while (!s1.empty()) {
        TreeNode* node = s1.top();
        s1.pop();
        s2.push(node);

        // 先将左子节点压入栈
        if (node->left) s1.push(node->left);
        // 再将右子节点压入栈
        if (node->right) s1.push(node->right);
    }

    // 最后从栈2中弹出节点并访问
    while (!s2.empty()) {
        std::cout << s2.top()->val << " ";
        s2.pop();
    }
}

广度优先算法(层次遍历):

#include <iostream>
#include <queue>

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

void levelOrderTraversal(TreeNode* root) {
    if (root == nullptr) return;  // 若根节点为空,直接返回

    std::queue<TreeNode*> q;  // 定义一个队列
    q.push(root);  // 将根节点入队

    while (!q.empty()) {
        TreeNode* node = q.front();  // 取队列头部的节点
        q.pop();  // 出队列

        std::cout << node->val << " ";  // 访问节点

        // 若有左子节点,将左子节点入队
        if (node->left != nullptr) {
            q.push(node->left);
        }

        // 若有右子节点,将右子节点入队
        if (node->right != nullptr) {
            q.push(node->right);
        }
    }
}

int main() {
    // 构造一个简单的二叉树
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);
    root->right->left = new TreeNode(6);
    root->right->right = new TreeNode(7);

    // 执行广度优先遍历
    std::cout << "二叉树的广度优先遍历结果: ";
    levelOrderTraversal(root);

    return 0;
}

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

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

相关文章

大学开学必备好物清单有哪些?开学必备清单大全,超详细版!

即将踏入大学校园的新生们&#xff0c;是否已经准备好迎接全新的挑战与机遇呢&#xff1f;在开学之前&#xff0c;将必备物品筹备妥当是极为重要的事情&#xff0c;因为这能够助力大家更为良好地适应大学生活。接下来&#xff0c;为大家提供一份实用的大学生开学必备物品清单&a…

性能测试面试题总结

最近这一年&#xff0c;对性能测试有了更多的认知。 压力、强度测试&#xff1a;在一定软硬件环境下&#xff0c;通过高负载的手段来使服务器资源&#xff08;强调服务器资源&#xff0c;硬件资源&#xff09;处于极限状态&#xff0c;测试系统在极限状态下长时间运行是否稳定…

大模型微调---qwen实战

一、Qwen大模型的介绍 Qwen是阿里云开发的大语言模型&#xff0c;整个qwen系列的模型&#xff0c;由base模型、rm模型、chat模型、code模型、math模型等等。 qwen采用chatml样式的格式来进行模型训练&#xff0c;chatml格式可以时模型有效区分各类信息&#xff0c;可以增强模…

blender插件库

插件安装教程&#xff1a;blender4.2中安装插件的方式-CSDN博客 blender官网插件库地址&#xff1a;Add-ons — Blender Extensions 1&#xff0c;ExtraObjects&#xff1a;提供更多网格形状&#xff0c; 链接:https://caiyun.139.com/m/i?2gov6Lw5RAib8 提取码:0ayj 复制内…

有向图的转置:算法分析与实现

有向图的转置:算法分析与实现 前言1. 邻接链表表示法2. 邻接矩阵表示法结论前言 在计算机科学中,图是一种非常重要的数据结构,用于表示对象之间的复杂关系。有向图(Directed Graph)是一种图,其边具有方向性。有向图的转置(Transpose)是一种基本操作,它将图中所有边的…

LLM面经(持续更新中)

Tokenizer Norm Batch Norm 好处 使得模型训练收敛的速度更快 每层的数据分布都不一样的话(解决Internal Covariance Shift)&#xff0c;将会导致网络非常难收敛和训练&#xff0c;而如果把每层的数据都在转换在均值为零&#xff0c;方差为1的状态下&#xff0c;这样每层数据…

第一个golang项目

第一个golang项目 开发环境安装golangVisual Studio Code安装golang语言插件初始化项目创建目录初始化golang配置 开始开发安装所需依赖创建main.go创建配置文件创建命令版本命令查看指定目录指定后缀文件并将指定内容替换为新内容 打包并运行 前因后果&#xff1a;因为工作需要…

不可错过的10款电脑监控软件推荐,电脑监控软件哪个好?宝藏安利

电脑监控软件已成为企业管理和家庭安全的重要工具。 无论是为了提升工作效率、保障信息安全&#xff0c;还是为了监督孩子的学习情况&#xff0c;一款优秀的电脑监控软件都能发挥巨大作用。 本文将为您推荐10款不可错过的电脑监控软件&#xff0c;并详细分析它们的优势与特点&…

Elastic Stack(三):Logstash介绍及安装

目录 1 Logstash介绍1.1 组件介绍1.2 Logstash 工作原理 2 Logstash安装2.1 logstash-源码包安装8.1.01、logstash安装2、创建配置文件3、启动4、配置快速启动文件 1 Logstash介绍 1.1 组件介绍 Logstash是一个开源数据收集引擎&#xff0c;具有实时管道功能。Logstash可以动…

财富趋势金融大模型已通过备案

财富趋势金融大模型已通过备案 8月28日晚&#xff0c;国内领先的证券软件与信息服务提供商——财富趋势&#xff0c;公布了其2024年上半年财务报告&#xff1a; 今年上半年&#xff0c;财富趋势营收1.48亿元&#xff0c;同比增长0.14%&#xff1b;实现归母净利润为1亿元&#x…

适用于 Windows 的文件恢复软件

我很遗憾我在 Windows中从 PC 中删除了数据并再次移动了它们。当我检查时&#xff0c;什么都没有。是否有任何 Windows 数据恢复软件&#xff0c;或者是否可以想象&#xff1f;我会看到任何援助的价值。 文档、图像、音频等数据文件可能会因意外删除、感染攻击、系统崩溃等不良…

mac os系统

各种各样的系统优缺点-CSDN博客 目录 一&#xff1a;mac os是什么系统&#xff1f;图形用户界面的革命性操作系统 二&#xff1a;mac os是什么系统&#xff1a;高性能和无缝衔接&#xff0c;功能丰富、安全可靠 三&#xff1a;mac os是什么系统&#xff1a;全新界面设计和卓…

Tomcat 环境配置及部署Web项目

一.环境 Java Tomcat 二.Java环境 1.下载安装JDK 2.修改及新建环境变量 3.查看Java 版本 三.Tomcat 环境 1.下载及解压Tomcat 2.配置环境变量 3.验证安装,运行startup.bat 访问&#xff1a;http://localhost:8080/ 三.Web项目 1.修改Tomcat配置文件 2.拷贝W…

Python将Latex公式插入到Word中

推荐一个库&#xff0c;可以使用python将Latex公式插入到Word中显示 使用pip进行安装: pip install latex2word 示例将如下公式插入到word 公式1&#xff1a; f(x) \int_{-\infty}^\infty \hat f(x)\xi\,e^{2 \pi i \xi x} \,\mathrm{d}\xi 公式2&#xff1a; \int x^{\mu}…

重生奇迹MU 小清新职业智弓MM

游戏中有一种令人迷醉的职业——智弓MM&#xff0c;她们以高超的射箭技能闻名于世。本文将为您介绍这个悠闲的小清新职业&#xff0c;在游戏中的特点以及如何成为一名出色的智弓MM。跟随我们一起探索这个奇妙而神秘的职业吧&#xff01; 悠闲的游戏节奏是游戏的初衷之一&#…

Dataease1.8.23 local本地安装

1、安装视频 手把手带你安装DataEase&#xff08;一&#xff09;Local模式部署 DataEase 免费开源BI工具 开源数据可视化分析工具 2、图文 安装模式 - DataEase 文档 注意点&#xff1a; 1、数据库&#xff1a;mysql 1&#xff09;my.cnf 新增配置&#xff1a; #忽略大小…

35岁以上程序员转行AI绘画领域:以Stablediffusion和Midjourney为舟,扬帆起航

前言 在技术浪潮的推动下&#xff0c;35岁的程序员面临职业转型的十字路口。AI绘画领域&#xff0c;以其独特的艺术创新和技术融合&#xff0c;为中年程序员提供了一个全新的舞台。利用AI工具如Stablediffusion和Midjourney&#xff0c;35岁以上的程序员可以在这个领域实现自我…

第二证券:三折折叠屏手机呼之欲出,14股业绩暴涨超200%

智能制作龙头大手笔投建机器人超级工厂 8月29日晚间&#xff0c;埃夫特发布公告称&#xff0c;公司拟运用自有或自筹资金在安徽省芜湖市经过购买土地用于出资制作埃夫特机器人超级工厂暨全球总部项目&#xff0c;根据初步测算&#xff0c;项目出资金额约人民币18.93亿元&#…

CAD二次开发IFoxCAD框架系列(25)- 自动加载和初始化的使用

自动加载&#xff0c;意思就是我们不需要每次重启都得要去输入netload加载软件&#xff0c;这个我们该怎么解决&#xff0c;CAD给我们提供了注册表的方式来进行加载&#xff0c;IFoxCAD给我们提供了非常便捷的操作注册表的方法。 namespace ifoxgse.Core.System;public static…

【算法】带你快速搞懂 二分查找算法问题(图解+练习)

目录 ❤️一&#xff1a;二分查找分析简介❤️ ❤️二&#xff1a;二分查找代码实现❤️ ​编辑 ❤️三&#xff1a;二分查找代码分析图解❤️ ❤️四&#xff1a;解决二分查找相关题型❤️ &#x1f495;4.1&#xff1a;二分查找题型一&#x1f495; &#x1f495;4.2&a…