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

news2024/11/18 3:37:04
  • 公开视频 -> 链接点击跳转公开课程
  • 博客首页 -> 链接点击跳转博客主页

目录

树的概念

结构特性

树的样式

树的存储

树的遍历

节点增删

二叉搜索树

平衡二叉树


树的概念

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

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

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

  • 节点(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.coloradmin.cn/o/1891079.html

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

相关文章

<电力行业> - 《第15课:电力领域(一)》

1 电网 发电厂与最终用电用户&#xff08;负荷&#xff09;往往相距很远&#xff0c;因此电力需要由电厂”输送“到最终用户&#xff0c;即“输电环节“&#xff0c;电流的输送往往导致因线路发热造成损耗&#xff0c;所以在输送的时候都是通过变电升高电压&#xff0c;让电流…

C语言刷题小记

前言 本篇博客和大家分享一些C语言的OJ题目&#xff0c;希望大家可以通过这些题目进一步提升自己的编程能力&#xff0c;如果你对本篇内容感兴趣&#xff0c;可以一键三连&#xff0c;多多关注&#xff0c;下面进入正文部分。 题目1 十六进制转十进制 描述 BoBo写了一个十六…

66.Python-web框架-Django-免费模板django-datta-able的分页的一种方式

目录 1.方案介绍 1.1实现效果 1.2django.core.paginator Paginator 类: Page 类: EmptyPage 和 PageNotAnInteger 异常: 1.3 templatetags 2.方案步骤 2.1创建一个common app 2.2创建plugins/_pagination.html 2.3 其他app的views.py查询方法 2.4在AIRecords.html里…

springboot旅游管理系统-计算机毕业设计源码16021

摘 要 本文旨在设计和实现一个基于Spring Boot框架的旅游管理系统。该系统通过利用Spring Boot的快速开发特性和丰富的生态系统&#xff0c;提供了一个高效、可靠和灵活的解决方案。系统将实现旅游景点信息的管理、线路规划、跟团游玩、旅游攻略、酒店信息管理、订单管理和用户…

html+css+js文章模板

图片 源代码在图片后面&#xff0c;点赞加关注&#xff0c;谢谢&#x1f604; 源代码 <!DOCTYPE html> <html lang"zh"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width,…

JSP实现简单的登录和注册

JSP实现登录和注册&#xff08;Map集合模拟数据库&#xff09; 1、login.jsp2、 loginSelect.jsp3、register.jsp4、 RegisterSelect.jsp5、 index.jsp 1、login.jsp login.jsp中username和password在LoginSelect.jsp验证是否一致使用session.setAttribute("login_msg&quo…

Android Studio初学者实例:ContentProvider读取手机通讯录

该实验是通过ContentProvider读取手机通讯录 知识点包含了RecyclerView控件、UriMatcher、ContentResolver 先看效果&#xff0c;显示手机通讯录 首先是界面的布局代码 activity_main59.xml <?xml version"1.0" encoding"utf-8"?> <LinearL…

[译]Reactjs性能篇

英文有限&#xff0c;技术一般&#xff0c;海涵海涵&#xff0c;由于不是翻译出身&#xff0c;所以存在大量的瞎胡乱翻译的情况&#xff0c;信不过我的&#xff0c;请看原文&#xff5e;&#xff5e; 原文地址&#xff1a;https://facebook.github.io/react/docs/advanced-per…

不同操作系统下的换行符

1. 关键字2. 换行符的比较3. ASCII码4. 修改换行符 4.1. VSCode 5. 参考文档 1. 关键字 CR LF CRLF 换行符 2. 换行符的比较 英文全称英文缩写中文含义转义字符ASCII码值操作系统Carriage ReturnCR回车\r13MacIntosh&#xff08;早期的Mac&#xff09;LinefeedLF换行/新行\…

Qt Q_ASSERT详解

Q_ASSERT详解 引言一、基本用法二、深入了解三、参考链接 引言 Q_ASSERT是 Qt 框架中的一个宏&#xff0c;用于在调试时检查某个条件是否为真。它是程序调试中的一个重要工具&#xff0c;有助于开发者在开发过程中及时发现并修复潜在的错误。 一、基本用法 只在使用 Qt 的 D…

暑期大数据人工智能学习-企业项目试岗实训开营

暑期企业项目-试岗实训活动全面开启啦 跟张良均老师学大数据人工智能 不仅可以提供实习证明&#xff0c;有需要话也可以提供实习鉴定报告 √54个热门案例拆解 √40项目实战课程 √27个项目可选 √4个项目方向

数据提取的奥秘

在数字化时代&#xff0c;数据提取作为连接原始数据与知识发现的桥梁&#xff0c;其重要性不言而喻。它不仅是数据分析和数据治理的基石&#xff0c;更是企业决策和业务优化的关键。以下是数据提取奥秘的详细解析&#xff1a; 一、数据提取的定义与意义 定义&#xff1a;数据…

怎样保存python文件

按下“CtrlS”键即可快速保存Python文件。 或者点击“File”&#xff0c;在下拉菜单中选择“Save”。 打开后我们就会看到这样的一个页面窗口了。 我们还能在这里进行路径的保存位置的查找。 然后在这里选择文件类型&#xff0c;并输入文件名。 接下来我们点击保存就可以完成操…

PyCharm远程开发配置(2024以下版本)

目录 PyCharm远程开发配置 1、清理远程环境 1.1 点击Setting 1.2 进入Interpreter 1.3 删除远程环境 1.4 删除SSH 2、连接远程环境 2.1 点击Close Project 2.2 点击New Project 2.3 项目路径设置 2.4 SSH配置 2.5 选择python3解释器在远程环境的位置 2.6 配置远程…

AI Agent当牛做马,办公自动化带来超级生产力|对话Laplace

成立仅9个月的AI初创公司拉普拉斯智能&#xff08;Laplace AI&#xff09;&#xff0c;已经用原生智能生产力操作平台帮助企业用户实现智能体落地了&#xff01; 平台名为拉普拉斯智能实验室&#xff08;Laplace AI Lab&#xff09;&#xff0c;入口统一&#xff0c;用自然语言…

2024-2025年本田维修电路图线路图接线图资料更新

此次更新了2024-2025年本田车系电路图资料&#xff0c;覆盖市面上99%车型&#xff0c;包括维修手册、电路图、新车特征、车身钣金维修数据、全车拆装、扭力、发动机大修、发动机正时、保养、电路图、针脚定义、模块传感器、保险丝盒图解对照表位置等等&#xff01; 汽修帮手汽…

小迪安全v2023 javaWeb项目

小迪安全v2023 javaWeb项目 大体上跟随小迪安全的课程&#xff0c;本意是记录自己的学习历程&#xff0c;不能说是完全原创吧&#xff0c;大家可以关注一下小迪安全&#xff0c;他讲的挺好的。 若有冒犯&#xff0c;麻烦私信移除。 已获得迪の认可&#xff0c;哈哈 文章目录…

【Spring Boot】关系映射开发(一):一对一映射

关系映射开发&#xff08;一&#xff09;&#xff1a;一对一映射 1.认识实体间关系映射1.1 映射方向1.2 ORM 映射类型 2.实现 “一对一” 映射2.1 编写实体2.1.1 新建 Student 实体2.1.2 新建 Card 实体 2.2 编写 Repository 层2.2.1 编写 Student 实体的 Repository2.2.2 编写…

轮换IP是什么?——深入了解轮换IP的特点

大家在日常上网时&#xff0c;可能听说过“轮换IP”这个词。那么&#xff0c;轮换IP到底是什么&#xff1f;它有哪些特点&#xff1f;今天&#xff0c;我们就来揭开轮换IP的神秘面纱。 什么是轮换IP&#xff1f; 简单来说&#xff0c;轮换IP是指定期更换上网时使用的IP地址。…

C++字体库开发二

Font: 字体大小&#xff0c;方向&#xff0c;变换 FontContext: 多语言&#xff0c;更新&#xff0c;基础字体&#xff0c;表情 FontDescription: 字重&#xff0c;子样式&#xff0c;名称&#xff0c;底色 FontDir: 字体目标 FontFace: Regular,Bold特殊字重 FontFamily: 字体…