数据结构 - C/C++ - 树

  • 公开视频 -> 链接点击跳转公开课程
  • 博客首页 -> 链接点击跳转博客主页

目录

树的概念

结构特性

树的样式

树的存储

树的遍历

节点增删

二叉搜索树

平衡二叉树


树的概念

  • 二叉树是树形结构,是一种非线性结构。

    • 非线性结构:在二叉树中,数据项的排列不是简单的线性序列,而是通过节点间的链接构成复杂的层次结构。

    • 受限节点数目:每个节点最多有两个子节点,这限定了树的分支宽度。

  • 节点(Node)

    • 数据域:保存或显示与节点相关联的信息。

    • 左子节点指针:指向左侧子节点的链接。

    • 右子节点指针:指向右侧子节点的链接。

  • 根节点(Root)

    • 节点是树结构的最顶端节点,它没有父节点,并且是二叉树结构的起点。

  • 父节点(Parent)

    • 与子节点相关联的上一级节点。

  • 子节点(Child)

    • 父节点指向的左子节点或者右子节点。

  • 叶子节点(Leaf)

    • 叶子节点是指没有任何子节点的节点。在树的结构中,叶子节点总是位于最底层。

  • 兄弟节点(Brother)

    • 在二叉树中,共享同一父节点的两个节点称为兄弟节点。

  • 节点的度

    • 节点分支数。

    • 度为0:节点没有子节点,即叶子节点。

    • 度为1:节点有一个子节点。

    • 度为2:节点有两个子节点。

  • 结点层度:根节点的层次为1,以此递增。

  • 树的深度:树种节点层次的最大值。

结构特性

  • 二叉树中第I层中最多存在2^(I - 1)的节点数量。

  • 二叉树中深度为I时最多存在2^I - 1的节点总数。

树的样式

  • 二叉树

  • 完美二叉树

    • 完美二叉树中,除了叶子节点外其余所有节点的度都有2。

    • 完美二叉树中,深度为I时节点数量为2^I - 1。

树的存储

  • 顺序存储

    • 基于数组 - 内存中使用连续的内存空间

    • 假设根节点编号为x

      • 左子节点编号为2 * x

      • 右子节点编号为2 * x + 1

  • 链式存储

    • 基于链表 - ListNode

树的遍历

  • 先序遍历 DLR 根节点 左子树 右子树

  • 中序遍历 LDR 左子树 根节点 右子树

  • 后序遍历 LRD 左子树 右子树 根节点

    • 示例代码

    #include <iostream>
    
    class TreeNode
    {
    public:
        char ch;
        TreeNode* Left;
        TreeNode* Righ;
        TreeNode(char value) : ch(value), Left(nullptr), Righ(nullptr) {}
    };
    
    void PreorderTraverse(TreeNode* T)
    {
        if (T == NULL) return;
        printf("%c ", T->ch);
        PreorderTraverse(T->Left);
        PreorderTraverse(T->Righ);
    }
    
    void InOrderTraverse(TreeNode* T)
    {
        if (T == NULL) return;
        InOrderTraverse(T->Left);
        printf("%c ", T->ch);
        InOrderTraverse(T->Righ);
    }
    
    void PostOrderTraverse(TreeNode* T)
    {
        if (T == NULL) return;
        PostOrderTraverse(T->Left);
        PostOrderTraverse(T->Righ);
        printf("%c ", T->ch);
    }
    
    int main()
    {
        //二叉树节点
    #if 1
        TreeNode* NodeA = new TreeNode('A');
        TreeNode* NodeB = new TreeNode('B');
        TreeNode* NodeC = new TreeNode('C');
        TreeNode* NodeD = new TreeNode('D');
        TreeNode* NodeE = new TreeNode('E');
        TreeNode* NodeF = new TreeNode('F');
        TreeNode* NodeG = new TreeNode('G');
        TreeNode* NodeH = new TreeNode('H');
        TreeNode* NodeI = new TreeNode('I');
    #endif
    
        //二叉树图解
        /*
                        A
                       / \
                      B   C
                     /   / \
                    D   E   F
                   / \   \
                  G   H   I
        */
    
        //二叉树关联
    #if 1
        NodeA->Left = NodeB;
        NodeB->Left = NodeD;
        NodeD->Left = NodeG;
        NodeD->Righ = NodeH;
    
        NodeA->Righ = NodeC;
        NodeC->Left = NodeE;
        NodeE->Righ = NodeI;
        NodeC->Righ = NodeF;
    #endif
    
        //二叉树遍历
    #if 1
        PreorderTraverse(NodeA);
        InOrderTraverse(NodeA);
        PostOrderTraverse(NodeA);
    #endif
    
        return 0;
    }
    

节点增删

  • 假如删除节点为叶子节点,直接删除节点并修正父节点对应指向为NULL。

  • 假如删除节点存在一个子节点,子节点替换被删除节点位置,并对应指向。

  • 假如删除节点存在两个子节点。

    //二叉树节点
    TreeNode* InsertNode = new TreeNode('J');
    TreeNode* TempNode = NodeA->Left;

    NodeA->Left = InsertNode;
    InsertNode->Left = TempNode;

二叉搜索树

  • 元素关联

    • 根节点的左子树不为空,则左子树上的所有节点的值均小于它根节点的值。

    • 根节点的右子树不为空,则右子树上的所有节点的值均大于它根节点的值。

    • 根节点的左子树以及右子树均为二叉排序树。

  • 元素排列

    • 中序遍历 LDR 左子树 根节点 右子树

    • 10 20 30 40 50 60 70 80 90

  • 元素搜索

    • 通过根节点按左子节点遍历下去为最小值节点。

    • 通过根节点按右子节点遍历下去为最大值节点。

    • 查找指定节点二分(左小右大)。

  • 删除节点

    • 删除节点为叶子节点 - 直接删除节点,不会对当前结构产生影响。

    • 删除节点仅存在左子树 - 删除节点左子树替换。

    • 删除节点仅存在右子树 - 删除节点右子树替换。

    • 删除节点同时存在左子树以及右子树 - 删除节点左子树内容挂在删除节点右子树中的左子节点,删除节点的右子节点替换被删除节点。

    • 示例代码

    #include <iostream>
    
    class TreeNode
    {
    public:
        int value;
        TreeNode* left;
        TreeNode* right;
        TreeNode(int Num) : value(Num), left(nullptr), right(nullptr){}
    };
    
    class BinarySearchTree
    {
    public:
    
        //插入节点
        TreeNode* Insert(TreeNode* Node, int value)
        {
            //50, 30, 20, 40, 70, 60, 80, 10, 90
    
            //空节点
            if (Node == NULL) return new TreeNode(value);
    
            //判断大小
            if (value < Node->value)
            {
                Node->left = Insert(Node->left, value);
            }
            else
            {
                Node->right = Insert(Node->right, value);
            }
    
            return Node;
        }
    
        //中序遍历
        void InOrderTraverse(TreeNode* Root)
        {
            if (Root == NULL) return;
            InOrderTraverse(Root->left);
            std::cout << Root->value << std::endl;
            InOrderTraverse(Root->right);
        }
    
        //查找节点
        TreeNode* Search(TreeNode* Node, int value)
        {
            if (Node == NULL) return NULL;
            if (Node->value == value) return Node;
    
            if (value < Node->value)
            {
                return Search(Node->left, value);
            }
            else
            {
                return Search(Node->right, value);
            }
    
        }
    
        //删除节点
        TreeNode* Delete(TreeNode* Root, int value)
        {
            if (Root == NULL) return NULL;
    
            //删除节点
            if (Root->value == value)
            {
                if (Root->left == NULL && Root->right == NULL)
                {
                    delete Root;
                    return NULL;
                }
                else if (Root->right == NULL && Root->left != NULL)
                {
                    TreeNode* retNode = Root->left;
                    delete Root;
                    return retNode;
                }
                else if (Root->right != NULL && Root->left == NULL)
                {
                    TreeNode* retNode = Root->right;
                    delete Root;
                    return retNode;
                }
                else
                {
                    TreeNode* lastLeft = Root->right;
                    while (lastLeft->left != NULL) lastLeft = lastLeft->left;
                    lastLeft->left = Root->left;
                    TreeNode* deleteNode = Root;
                    Root = Root->right;
                    delete deleteNode;
                    return Root;
                }
    
            }
    
            if (Root->value > value)
            {
                Root->left = Delete(Root->left, value);
            }
    
            if (Root->value < value)
            {
                Root->right = Delete(Root->right, value);
            }
    
            return NULL;
        }
    };
    
    int main()
    {
        BinarySearchTree BST;
        TreeNode* Root = NULL;
    
        int Arr[] = {30, 20, 40, 35,70, 60, 80, 10, 90 };
    
        Root = BST.Insert(Root, 50);
        for (size_t i = 0; i < sizeof(Arr) / sizeof(Arr[0]); i++)
        {
            BST.Insert(Root, Arr[i]);
        }
    
        BST.InOrderTraverse(Root);
    
        TreeNode* Temp = BST.Search(Root, 35);
        BST.Delete(Root, 70);
    
        return 0;
    }
    

平衡二叉树

  • 平衡二叉树

    • 二叉排序树。

    • 任何一个节点的左子树以及右子树都是平衡二叉树。

    • 任何一个节点的左子树与右子树的高度差值的绝对值不能大于一。

  • 平衡因子

    • 节点的平衡因子为其节点左子树的高度减去其右子树的高度(0/1/-1)。

  • 最小不平衡子树

    • 二叉树中插入节点时,距离插入节点位置最近的BF值大于一的节点作为最小不平衡子树。

  • 节点失衡

    • 二叉树插入节点时,出现平衡因子绝对值大于一的最小不平衡子树。

    • 通过“旋转”来修正最小不平衡子树。

  • 旋转方式

    • 左旋

      • 失衡节点的右子节点作为新的根节点。

      • 将失衡节点作为新的根节点的左子节点。

      • 新根节点如果存在左子树则转到旧根节点右子树下。

    • 右旋

      • 失衡节点的左子节点作为新的根节点。

      • 将失衡节点作为新的根节点的右子节点。

      • 新根节点如果存在右子树则转到旧根节点左子树下。

    • 旋转类型

      • LL(单右旋转)

        • 触发 - 插入节点发生在左子树的左侧

        • 操作 - 失衡节点的左子节点作为新的根节点,将失衡节点作为新的根节点的右子节点。

        • 图解

                 3          2
                /          / \
               2          1   3
              / 
             1    
          
            - RR(单左旋转)
          
              - 触发 - 插入节点发生在右子树的右侧
          
              - 操作 - 失衡节点的右子节点作为新的根节点,将失衡节点作为新的根节点的左子节点。
          
              - 图解
          
                ```Objective-C++
          
                      3              6
                       \            / \
                        6          3   8
                       / \         \
                      5   8         5
          
  • 模拟旋转

    • 30 20 10 40 50 60 70 100 90

    • #include <stdio.h>
      #include <stdlib.h>
      #include <memory.h>
      
      //节点结构
      typedef struct _Node
      {
          int value;
          int height;
          struct _Node* left;
          struct _Node* right;
      }Node;
      
      //节点高度
      int GetNodeHeight(Node* node)
      {
          if (node == NULL) return NULL;
          return node->height;
      }
      
      //平衡因子
      int GetNodeBalanceFactor(Node* node)
      {
          if (node == NULL) return NULL;
          return GetNodeHeight(node->left) - GetNodeHeight(node->right);
      }
      
      //左旋处理
      Node* LeftRotate(Node* node)
      {
          //失衡节点的右子节点作为新的根节点
          Node* Root = node->right;
          Node* Temp = Root->left;
      
          //将失衡节点作为新的根节点的左子节点
          Root->left = node;
      
          //新根节点如果存在左子树则转到旧根节点右子树下
          node->right = Temp;
      
          //修正节点高度
          node->height = max(GetNodeHeight(node->left), GetNodeHeight(node->right)) + 1;
          Root->height = max(GetNodeHeight(Root->left), GetNodeHeight(Root->right)) + 1;
      
          return Root;
      }
      
      //右旋处理
      Node* RightRotate(Node* node)
      {
          //失衡节点的左子节点作为新的根节点
          Node* Root = node->left;
          Node* Temp = Root->right;
      
          //将失衡节点作为新的根节点的右子节点
          Root->right = node;
      
          //新根节点如果存在右子树则转到旧根节点左子树下
          node->left = Temp;
      
          //修正节点高度
          node->height = max(GetNodeHeight(node->left), GetNodeHeight(node->right)) + 1;
          Root->height = max(GetNodeHeight(Root->left), GetNodeHeight(Root->right)) + 1;
      
          return Root;
      }
      
      //创建节点
      Node* NewNode(int value)
      {
          Node* node = malloc(sizeof(Node));
          if (node == NULL) return NULL;
          memset(node, 0, sizeof(Node));
          node->value = value;
          node->height = 1;
          node->left = NULL;
          node->right = NULL;
          return node;
      }
      
      //插入节点
      Node* Insert(Node* Root, int value)
      {
          //空的结构
          if (Root == NULL) return NewNode(value);
      
          //节点关联
          if (Root->value > value)
          {
              Root->left = Insert(Root->left, value);
          }
          else if (Root->value < value)
          {
              Root->right = Insert(Root->right, value);
          }
          else
          {
              return Root;
          }
      
          //节点高度
          Root->height = max(GetNodeHeight(Root->left), GetNodeHeight(Root->right)) + 1;
      
          //节点失衡
          int nBalance = GetNodeBalanceFactor(Root);
      
          //左左
          if (nBalance > 1 &&  value < Root->left->value)
          {
              return RightRotate(Root);
          };
      
          //右右
          if (nBalance < -1 && value > Root->right->value)
          {
              return LeftRotate(Root);
          }
      
          //左右
          if (nBalance > 1 && value > Root->left->value)
          {
              Root->left = LeftRotate(Root->left);
              return RightRotate(Root);
          }
      
          //右左
          if (nBalance < -1 && value < Root->right->value)
          {
              Root->right = RightRotate(Root->right);
              return LeftRotate(Root);
          }
      
          return Root;
      }
      
      int main()
      {
          Node* Root = NULL;
          Root = Insert(Root, 30);
          Root = Insert(Root, 20);
          Root = Insert(Root, 25);
      
          return 0;
      }
      
      • 示例代码        

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

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

相关文章

分享一款可编辑本地电脑文件的在线编辑器

背景 之前见过在线版的VSCode&#xff0c;被惊讶到了。网页上竟然可以编辑电脑本地的文件&#xff0c;打破了网页无法编辑本地电脑文件的限制。一直好奇怎么做的。抽空研究了一下&#xff0c;然后发现其实也不难。 分析 先给大家介绍一下这款在线编辑器的效果。 左侧栏为文件…

森马基于MaxCompute+Hologres+DataWorks构建数据中台

讲师&#xff1a;晋银龙 浙江森马数仓高级经理 本次案例主要分享森马集团面对多年自建的多套数仓产品体系&#xff0c;通过阿里云MaxComputeHologresDataWorks统一数仓平台&#xff0c;保障数据生产稳定性与数据质量&#xff0c;减少ETL链路及计算时间&#xff0c;每年数仓整体…

平衡二叉查找树和多路查找树

平衡二叉查找树 普通平衡二叉查找树 平衡二叉树定义是按照有序排列成树状&#xff0c;左子树数据大于右子树&#xff0c;任意节点的左右子树高度不能大于1 优点&#xff1a;可以保证绝对的平衡 缺点&#xff1a;当进行删除节点和新增节点&#xff0c;树进行自平衡的时候&…

计算机网络网络层复习题2

一. 单选题&#xff08;共22题&#xff0c;100分&#xff09; 1. (单选题)如果 IPv4 数据报太大&#xff0c;会在传输中被分片&#xff0c;对分片后的数据报进行重组的是&#xff08; &#xff09;。 A. 中间路由器B. 核心路由器C. 下一跳路由器D. 目的主机 我的答案: D:目的…

RocketMQ源码学习笔记:Producer启动流程

这是本人学习的总结&#xff0c;主要学习资料如下 马士兵教育rocketMq官方文档 目录 1、Overview1.1、创建MQClientInstance1.1.1、检查1.1.1、MQClientInstance的ID 1.2、MQClientInstance.start() 1、Overview 这是发送信息的代码样例&#xff0c; DefaultMQProducer produ…

Qt中使用MySQL数据库详解,好用的模块类封装

本文将详细介绍如何在Qt应用程序中集成MySQL数据库&#xff0c;并封装实现好用的mysql数据库操作类。包括环境准备、连接数据库、执行查询及异常处理等关键步骤&#xff0c;同时包含mysql驱动的编译。分享给有需要的小伙伴&#xff0c;喜欢的可以点击收藏。 目录 环境准备 项…

MySql Innodb锁机制

锁概述 undo log版本链 Read View机制实现的MVCC多版本并发控制&#xff0c;可以防止事务并发读写同一数据时出现的脏读不可重复读幻读问题。但除脏读不可重复读幻读问题外&#xff0c;并发读写同一数据还有脏写问题。就是当多个事务并发更新同一条数据时&#xff0c;此时就可…

【CT】LeetCode手撕—199. 二叉树的右视图

目录 题目1- 思路2- 实现⭐199. 二叉树的右视图——题解思路 3- ACM 实现 题目 原题连接&#xff1a;199. 二叉树的右视图 1- 思路 使用二叉树的层序遍历 2- 实现 ⭐199. 二叉树的右视图——题解思路 class Solution {public List<Integer> rightSideView(TreeNode ro…

Let‘s Encrypt 申请免费 SSL 证书(每隔60天自动更新证书)

文章目录 官网文档简介安装 Nginxacme.sh生成证书智能化生成证书 安装证书查看已安装证书更新证书 官网 https://letsencrypt.org/zh-cn/ 文档 https://letsencrypt.org/zh-cn/docs/ 简介 Let’s Encrypt 是一个非营利组织提供的免费SSL/TLS证书颁发机构&#xff0c;旨在促…

如何在 Windows 10 或 11 中恢复已删除的文件

您在 Windows PC 上找不到某个文件&#xff0c;并且您觉得可能已将其删除。我们都遇到过这种情况。但与其抱怨&#xff0c;不如尝试恢复它。假设您已经搜索过回收站&#xff0c;但一无所获&#xff0c;那么是时候求助于一个好的恢复工具了。 微软提供了自己的命令行恢复程序&a…

Vite: 插件流水线之核心编译能力

概述 Vite 在开发阶段实现了一个按需加载的服务器&#xff0c;每一个文件请求进来都会经历一系列的编译流程&#xff0c;然后 Vite 会将编译结果响应给浏览器。在生产环境下&#xff0c;Vite 同样会执行一系列编译过程&#xff0c;将编译结果交给 Rollup 进行模块打包这一系列…

Node端使用工作线程来解决日志开销-处理IO密集型任务

我们的BBF层很多时候会作为中间层处理后端到前端的数据&#xff0c;当然大部分时候都只是作为请求 / 响应的数据组装中心&#xff0c;但是有一个插件是怎么都绕不过去的&#xff1a;Log4js。 内部我们在Node层打印了很多日志。结果这周仔细分析了一下服务器处理请求到响应的中间…

excel数据大小显示竟然有最大限制,限制32,767,实际限制32759

Excel 单元格在显示数据时确实存在一些限制&#xff0c;这些限制主要与单元格的宽度和高度有关&#xff0c;而不是存储数据的大小。以下是一些主要的限制&#xff1a; 1. **列宽和行高**&#xff1a;Excel 单元格的显示大小取决于列宽和行高。如果单元格中的数据超出了设定的列…

C# Winform项目中简单使用Sqlite并在DataGridview中显示

1. SQLite概述 1.1 什么是 SQLite&#xff1f; SQLite是一个进程内的库&#xff0c;实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库&#xff0c;这意味着与其他数据库不一样&#xff0c;您不需要在系统中配置。 1.2 为什么要用 …

vmware虚拟机安装openEuler

一、openEuler简介 openEuler是一款开源操作系统。当前openEuler内核源于Linux&#xff0c;支持鲲鹏及其它多种处理器&#xff0c;能够充分释放计算芯片的潜能&#xff0c;是由全球开源贡献者构建的高效、稳定、安全的开源操作系统&#xff0c;适用于数据库、大数据、云计算、…

游戏AI的创造思路-技术基础-自然语言处理

自然语言处理-可以对游戏AI特别是RPG类、语言类游戏进行“附魔”&#xff0c;开发出“随机应变”和你聊天的“女友”、“队友”或者是根据你定义的文本库来用接近自然语言的生成“语言”&#xff0c;推动游戏情景在受控范围内前进 目录 1. 自然语言处理定义 2. 发展历史 3. …

k8s部署单节点redis

一、configmap # cat redis-configmap.yaml apiVersion: v1 kind: ConfigMap metadata:name: redis-single-confignamespace: redis data:redis.conf: |daemonize nobind 0.0.0.0port 6379tcp-backlog 511timeout 0tcp-keepalive 300pidfile /data/redis-server.pidlogfile /d…

高考服务系统

摘 要 每年有大批考生在进行填写高考志愿时并不很清楚自己的高考分数适合那些高校以及专业。高考考生面临着未被高校录取&#xff0c;被调剂专业&#xff0c;甚至可能复读的问题。若能让考生轻松查询到高校录取、高校专业、高校招生等相关信息&#xff0c;能减少很大一部分考生…

《后端程序猿 · Caffeine 本地缓存》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻一周&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

SolrCloud Autoscaling 自动添加副本

SolrCloud Autoscaling 自动添加副本 前言 问题描述 起因是这样的&#xff0c;我在本地调试 Solr 源码&#xff08;版本 7.7.3&#xff09;&#xff0c;用 IDEA 以 solrcloud 方式启动了 2 个 Solr 服务&#xff0c;如下所示&#xff1a; 上图的启动参数 VM Options 如下&am…