数据结构-树(树基本实现C++)

2023-10-27

​树形结构是一种重要的非线性数据结构。其中树和二叉树最为常用,直观看来树是以分支关系定义的层次结构。树形结构是我们平时比较熟悉的,比如文件夹目录、公司组织关系等。在计算机领域也得到广泛的应用,编译程序就是以树来表示源程序的语法结构。

二叉树是一种特殊的树形结构,他的特点是每个节点至多只有两颗子树,并且,子树有左右之分,顺序不能颠倒。

 

树形结构里边还有很多的知识点,我不在这里做文字上的解释,这里只是针对二叉树的相关特点,用C++分别实现基于数组和基于链表两种方式的二叉树,以及实现二叉树的三种遍历方式。

基于数组的二叉树基本实现:
 

class Tree
{
public:
  explicit Tree(int size, int* pRoot)
{
    m_iSize = size;
    m_pTree = new int[m_iSize];
    memset(m_pTree, 0, sizeof(int)*m_iSize);
    m_pTree[0] = *pRoot;
  }
  ~Tree()
  {
    delete[]m_pTree;
    m_pTree = NULL;
  }
  int* SearchNode(int index)
{
    if (index<0 && index>m_iSize)
    {
      return NULL;
    }
​
    return &m_pTree[index];
  }
​
  //direction 1:left 2:right
  bool AddNode(int index, int direction, int* pNode)
{
    if (index<0 && index>m_iSize)
    {
      return false;
    }
​
    if (m_pTree[index] == 0)
    {
      return false;
    }
​
    if (index * 2 + direction > m_iSize)
    {
      return false;
    }
​
    m_pTree[index * 2 + direction] = *pNode;
    return true;
  }
​
  bool DeleteNode(int index, int* pNode)
{
    if (index<0 && index>m_iSize)
    {
      return false;
    }
​
    *pNode = m_pTree[index];
    m_pTree[index] = 0;
    return true;
  }
  void Traverse()
{
    for (int i = 0; i < m_iSize; i++)
    {
      cout << m_pTree[i] << " ";
    }
    cout << endl;
  }
private:
  int* m_pTree;
  int m_iSize;
};

基于链表的二叉树实现:

#ifndef _CTREE_H_
#define _CTREE_H_
#include<iostream>
​
using namespace std;
class TreeNode
{
public:
  int index; //坐标
  char data; //数据
  TreeNode *pLChild; //左节点
  TreeNode *pRChild; //右节点
  TreeNode *pParent; //父节点
public:
  TreeNode() : index(0), 
    data('0'), 
    pLChild(NULL), 
    pRChild(NULL), 
    pParent(NULL) {}
​
  TreeNode(int iNodeIndex, char val) : index(iNodeIndex), 
    data(val), 
    pLChild(NULL), 
    pRChild(NULL), 
    pParent(NULL) {}
​
  ~TreeNode()
  {
    //if (pLChild)
    //{
    //  delete pLChild;
    //  pLChild = NULL;
    //}
​
    //if (pRChild)
    //{
    //  delete pRChild;
    //  pRChild = NULL;
    //}
​
    //if (pParent)
    //{
    //  delete pParent;
    //  pParent = NULL;
    //}
  }
​
  //TreeNode(const TreeNode& node)
  //{
  //  index = node.index;
  //  data = node.data;
​
  //  delete pLChild;
  //  if (node.pLChild)
  //  {
  //    pLChild = new TreeNode(node.pLChild->data, node.pLChild->index);
  //  }
​
  //  delete pRChild;
  //  if (node.pRChild)
  //  {
  //    pRChild = new TreeNode(node.pRChild->data, node.pRChild->index);
  //  }
​
  //  delete pParent;
  //  if (node.pParent)
  //  {
  //    pParent = new TreeNode(node.pParent->data, node.pParent->index);
  //  }
  //}
​
  //TreeNode& operator=(const TreeNode& node) 
  //{
  //  if (this == &node)
  //  {
  //    return *this;
  //  }
​
  //  index = node.index;
  //  data = node.data;
​
  //  delete pLChild;
  //  if (node.pLChild)
  //  {
  //    pLChild = new TreeNode(node.pLChild->data, node.pLChild->index);
  //  }
  //
  //  delete pRChild;
  //  if (node.pRChild)
  //  {
  //    pRChild = new TreeNode(node.pRChild->data, node.pRChild->index);
  //  }
​
  //  delete pParent;
  //  if (node.pParent)
  //  {
  //    pParent = new TreeNode(node.pParent->data, node.pParent->index);
  //  }
​
  //  return *this;
  //}
​
  TreeNode* SearchNode(int iNodeIndex)
{
    if (index == iNodeIndex)
    {
      return this;
    }
​
    TreeNode *temp = NULL;
    if (pLChild)
    {
      if (pLChild->index == iNodeIndex)
      {
        return pLChild;
      }
      else
      {
        temp = pLChild->SearchNode(iNodeIndex);
        if (temp)
        {
          return temp;
        }
      }
    }
    if (pRChild)
    {
      if (pRChild->index == iNodeIndex)
      {
        return pRChild;
      }
      else
      {
        temp = pRChild->SearchNode(iNodeIndex);
        if (temp)
        {
          return temp;
        }
      }
​
    }
​
    return NULL;
  }
​
  void DeleteNode()
{
    if (pLChild != NULL)
    {
      pLChild->DeleteNode();
    }
​
    if (pRChild != NULL)
    {
      pRChild->DeleteNode();
    }
​
    if (pParent != NULL)
    {
      if (this == pParent->pLChild)
        pParent->pLChild = NULL;
      if (this == pParent->pRChild)
        pParent->pRChild = NULL;
    }
​
    delete this;
  }
​
  //先序遍历 根左右
  void PreOrderTraversal()
{
    cout << data << " ";
​
    if (pLChild)
    {
      pLChild->PreOrderTraversal();
    }
​
    if (pRChild)
    {
      pRChild->PreOrderTraversal();
    }
  }
​
  //中序遍历 左根右
  void InOrderTraversal()
{
    if (pLChild)
    {
      pLChild->InOrderTraversal();
    }
​
    cout << data << " ";
​
    if (pRChild)
    {
      pRChild->InOrderTraversal();
    }
  }
​
  //后序遍历 左右根
  void PostOrderTraversal()
{
    if (pLChild)
    {
      pLChild->PostOrderTraversal();
    }
​
    if (pRChild)
    {
      pRChild->PostOrderTraversal();
    }
​
    cout << data << " ";
  }
​
  //禁止拷贝、赋值
private:
  TreeNode(const TreeNode& node);
  TreeNode& operator=(const TreeNode& node);
};
​
//基于链表的二叉树基本实现和遍历
class CTree
{
public:
  CTree(TreeNode *pNode = NULL)
  {
    //创建树默认先创建根节点
    m_pRoot = new TreeNode();
    if (!m_pRoot)
    {
      throw "根节点申请内存失败";
      return;
    }
​
    if (pNode)
    {
      m_pRoot->index = 0;
      m_pRoot->data = pNode->data;
    }
    else
    {
      m_pRoot->index = 0;
      m_pRoot->data = '0';
    }
  }
​
  ~CTree()
  {
    m_pRoot->DeleteNode();
  }
​
  TreeNode* SearchNode(int index)
{
    return m_pRoot->SearchNode(index);
  }
​
  bool AddNode(int index, int direction, TreeNode *pNode)
{
    if (!pNode)
    {
      cout << "插入失败!新增的节点值为空。" << endl;
      return false;
    }
​
    TreeNode *temp = SearchNode(index);
    if (!temp)
    {
      cout << pNode->data << "插入失败!找不到传入下标对应的父节点。" << endl;
      return false;
    }
​
    TreeNode *node = new TreeNode();
    if (!node)
    {
      cout << pNode->data << "插入失败!新的节点申请内存失败。" << endl;
      return false;
    }
​
    node->index = pNode->index;
    node->data = pNode->data;
    node->pParent = temp;
​
    if (1 == direction)
    {
      temp->pLChild = node;
    }
    else if (2 == direction)
    {
      temp->pRChild = node;
    }
    else
    {
      cout << pNode->data << "插入失败!。direction参数错误:1为左节点,2为右节点" << endl;
    }
    return true;
  }
​
  bool DeleteNode(int index, TreeNode *pNode)
{
    TreeNode *temp = SearchNode(index);
    if (!temp)
    {
      cout << "删除失败!找不到传入下标对应的节点。" << endl;
      return false;
    }
​
    if (pNode)
    {
      pNode->index = temp->index;
      pNode->data = temp->data;
    }
​
    temp->DeleteNode();
​
    return true;
  }
​
  //先序遍历-递归
  void Recursive_PreOrderTraversal()
{
    m_pRoot->PreOrderTraversal();
  }
​
  //中序遍历-递归
  void Recursive_InOrderTraversal()
{
    m_pRoot->InOrderTraversal();
  }
​
  //后序遍历-递归
  void Recursive_PostOrderTraversal()
{
    m_pRoot->PostOrderTraversal();
  }
​
  //先序遍历-非递归
  void PreOrderTraversal()
{
    int stackTop = -1;
    TreeNode* nodeStack[10];
    TreeNode *pMove = /*new TreeNode(m_pRoot->index, m_pRoot->data)*/m_pRoot;
    while (stackTop != -1 || pMove)
    {
      while (pMove)
      {
        cout << pMove->data << " ";
        nodeStack[++stackTop] = pMove;
        pMove = pMove->pLChild;
      }
​
      if (stackTop != -1)
      {
        pMove = nodeStack[stackTop];
        stackTop--;
        pMove = pMove->pRChild;
      }
    }
  }
​
​
  //中序遍历-非递归
  void InOrderTraversal()
{
    int stackTop = -1;
    TreeNode* nodeStack[10];
    TreeNode *pMove = /*new TreeNode(m_pRoot->index, m_pRoot->data)*/m_pRoot;
    while (stackTop != -1 || pMove)
    {
      //
      while (pMove)
      {
        nodeStack[++stackTop] = pMove;
        pMove = pMove->pLChild;
      }
​
      if (stackTop != -1)
      {
        pMove = nodeStack[stackTop--];
        cout << pMove->data << " ";
        pMove = pMove->pRChild;
      }
    }
  }
​
  //后序遍历-非递归
  void PostOrderTraversal()
{
    int stackTop = -1;
    TreeNode* nodeStack[10];
    TreeNode* pMove = /*new TreeNode(m_pRoot->index, m_pRoot->data)*/m_pRoot;
    TreeNode* pLastVisit = NULL;
​
    while (pMove)
    {
      nodeStack[++stackTop] = pMove;
      pMove = pMove->pLChild;
    }
    while (stackTop != -1)
    {
      pMove = nodeStack[stackTop--];
      if (pMove->pRChild == NULL || pMove->pRChild == pLastVisit)
      {
        cout << pMove->data << " ";
        pLastVisit = pMove;
      }
      else
      {
        nodeStack[++stackTop] = pMove;
        pMove = pMove->pRChild;
        while (pMove)
        {
          nodeStack[++stackTop] = pMove;
          pMove = pMove->pLChild;
        }
      }
    }
​
  }
private:
  TreeNode *m_pRoot; //根节点
};
​
#endif // !_CTREE_H_

测试二叉树结构图:

 

测试主函数main.cpp实现:

int main(int argc, char**argv)
{
  TreeNode nodeA(0, 'A');
  TreeNode nodeB(1, 'B');
  TreeNode nodeC(2, 'C');
  TreeNode nodeD(3, 'D');
  TreeNode nodeE(4, 'E');
  TreeNode nodeF(6, 'F');
  TreeNode nodeG(9, 'G');
​
  CTree *tree = new CTree(&nodeA);
  tree->AddNode(0, 1, &nodeB);
  tree->AddNode(0, 2, &nodeC);
  tree->AddNode(1, 1, &nodeD);
  tree->AddNode(1, 2, &nodeE);
  tree->AddNode(2, 2, &nodeF);
  tree->AddNode(4, 1, &nodeG);
​
  cout << "递归遍历: " << endl;
  tree->Recursive_PreOrderTraversal();
  cout << endl;
  tree->Recursive_InOrderTraversal();
  cout << endl;
  tree->Recursive_PostOrderTraversal();
  cout << endl;
​
  cout << "非递归遍历: " << endl;
  tree->PreOrderTraversal();
  cout << endl;
  tree->InOrderTraversal();
  cout << endl;
  tree->PostOrderTraversal();
  cout << endl;
​
  delete tree;
​
  system("pause");
  return 0;
}

 

测试结果:

 

 

 

--|END|--


 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构-树(树基本实现C++) 的相关文章

  • 在 VS2017 下使用 Conan 和 CMake 项目进行依赖管理

    我正在尝试使用 CMake 与 VS2017 集成为 C 设置一个开发环境 以便在 Linux x64 下进行编译 为了更好地管理依赖关系 我选择使用 Conan 但我对这个软件还很陌生 我想知道让 VS2017 识别项目依赖关系的最佳方法
  • 内联函数/方法

    声明 内联函数必须在调用之前定义 这个说法正确吗 EDIT 该问题最初是德语 内联功能穆森 弗 伊赫雷姆 奥夫鲁夫定义 sein 也许它对任何人都有帮助 是的 它是正确的 但只是部分正确 它可能正确地重新构建如下 内联函数必须在每个翻译单位
  • 将字节数组转换为托管结构

    更新 这个问题的答案帮助我编写了开源项目GitHub 上的 AlicanC 现代战争 2 工具 https github com AlicanC AlicanC s Modern Warfare 2 Tool 你可以看到我是如何阅读这些数据
  • C 程序从连接到系统的 USB 设备读取数据

    我正在尝试从连接到系统 USB 端口的 USB 设备 例如随身碟 获取数据 在这里 我可以打开设备文件并读取一些随机原始数据 但我想获取像 minicom teraterm 这样的数据 请让我知道我可以使用哪些方法和库来成功完成此操作以及如
  • 如何尝试/捕获所有异常

    我正在完成由其他人启动的 UWP 应用程序 该应用程序经常崩溃 我总是陷入困境应用程序 at if global System Diagnostics Debugger IsAttached global System Diagnostic
  • C# 正则表达式用于查找 中具有特定结尾的链接

    我需要一个正则表达式模式来查找字符串 带有 HTML 代码 中的链接 以获取文件结尾如 gif 或 png 的链接 示例字符串 a href site com folder picture png target blank picture
  • 选择列表逻辑应位于 ASP.NET MVC、视图、模型或控制器中的什么位置?

    我觉得我的问题与这个问题很接近 但我想对这样的代码应该放在哪里进行更一般的讨论 Asp Net MVC SelectList 重构问题 https stackoverflow com questions 2149855 asp net mv
  • 获取尚未实例化的类的函数句柄

    我对 C 相当陌生 我想做的事情可能看起来很复杂 首先 我想获取一些函数的句柄以便稍后执行它们 我知道我可以通过以下方式实现这一目标 List
  • 如何生成 appsettings..json 文件?

    我有一个 ASP NET Core 2 WebAPI 它将部署在以下环境中 INT QA STAGE 生产环境 基于上述 我需要有appsettings
  • 劫持系统调用

    我正在编写一个内核模块 我需要劫持 包装一些系统调用 我正在暴力破解 sys call table 地址 并使用 cr0 来禁用 启用页面保护 到目前为止一切顺利 一旦完成 我将公开整个代码 因此如果有人愿意 我可以更新这个问题 无论如何
  • 从成员函数指针类型生成函子

    我正在尝试简化 通过make fn 预处理参数的函子的生成 通过wrap 对于 arity 的成员函数n 生成函子基本上可以工作 但到目前为止只能通过显式指定成员函数的参数类型来实现 现在我想从它处理的成员函数类型生成正确的函子 struc
  • C# 委托责任链

    为了我的理解目的 我实现了责任链模式 Abstract Base Type public abstract class CustomerServiceDesk protected CustomerServiceDesk nextHandle
  • 默认析构函数做了多少事情

    C 类中的默认析构函数是否会自动删除代码中未显式分配的成员 例如 class C public C int arr 100 int main void C myC new C delete myC return 0 删除 myC 会自动释放
  • tabcontrol selectedindex 更改事件未被触发 C#

    嘿伙计们 我有一个很小的问题 请参阅下面的代码 this is main load private void Form1 Load object sender EventArgs e tabAddRemoveOperator Selecte
  • asp.net网格分页的SQL查询

    我在用iBatis and SQLServer 使用偏移量和限制进行分页查询的最佳方法是什么 也许我添加该列ROW NUMBER OVER ORDER BY Id AS RowNum 但这只会阻止简单查询的数据访问 在某些情况下 我使用选择
  • ASP.NET JQuery AJAX POST 返回数据,但在 401 响应内

    我的应用程序中有一个网页 需要调用我设置的 Web 服务来返回对象列表 这个调用是这样设置的 document ready function var response ajax type POST contentType applicati
  • 初始化 LPCTSTR /LPCWSTR [重复]

    这个问题在这里已经有答案了 我很难理解并使其正常工作 基本上归结为我无法成功初始化这种类型的变量 它需要有说的内容7 2E25DC9D 0 USB003 有人可以解释 展示这种类型的正确初始化和类似的值吗 我已查看此站点上的所有帮助 将项目
  • C 中带有指针的结构的内存开销[重复]

    这个问题在这里已经有答案了 我意识到当我的结构包含指针时 它们会产生内存开销 这里有一个例子 typedef struct int num1 int num2 myStruct1 typedef struct int p int num2
  • 受限 AppDomain 中的代码访问安全异常

    Goal 我需要在权限非常有限的 AppDomain 中运行一些代码 它不应该访问任何花哨或不安全的内容 except对于我在其他地方定义的一些辅助方法 我做了什么 我正在创建一个具有所需基本权限的沙箱 AppDomain 并创建一个运行代
  • OSError: [WinError 193] %1 不是有效的 Win32 应用程序,同时使用 CTypes 在 python 中读取自定义 DLL

    我正在尝试编写用 python 封装 C 库的代码 我计划使用 CTypes 来完成此操作 并使用 Visual Studio 来编译我的 DLL 我从一个简单的函数开始 在 Visual Studio 内的标头中添加了以下内容 然后将其构

随机推荐

  • osg学习(四十)osg::Viewer的realize创建窗体的几种方式

    能够根据屏幕数 创建不同位置的窗口 void Viewer realize 在某一个屏幕上创建无边框窗口 在某一个屏幕上创建正常窗口 在所有屏幕上创建正常窗口 一个窗口 窗口位置可以跨屏幕 osgViewer SingleWindow实现
  • promise笔记

    promise笔记 以下笔记主要参考axios官网里的promise教程 写在这里方便以后复习或者查找 Promise javascript info 可以通过这个链接进行学习 更加详细 文章目录 promise笔记 1 介绍 2 基本语法
  • 将字符串格式的时间格式化

    时间格式化 param Number date 时间戳 param DateString fmt 时间格式 dateFormat yyyy MM dd hh mm ss S gt 2016 03 12 20 13 32 232 return
  • Object.create 以及 Object.setPrototypeOf

    第一部分 Object crate 方法是es5中的关于原型的方法 这个方法会使用指定的原型对象以及属性去创建一个新的对象 语法 Object create proto propertiesObjecy 参数 proto 必须的 一个对象
  • 云计算简介

    什么是 云 迁移至云端 在云中运行 在云中存储 从云端访问 当今时代 似乎一切都在 云 中进行 但是 云 究竟是一个什么样的概念 简单来说 云就是互联网连接的另一端 你可以从云端访问各种应用程序和服务 也可以在云端安全存储你的数据 云 之所
  • 扫描二维码 跳转到小程序指定页面

    注意 必须发布代码之后才能使用扫描二维码跳转 规则 1 二维码规则 填写你要生成的二维码的链接 2 小程序功能页面 要跳转的小程序的页面 3 测试链接 也填同样的链接 4 将上面的链接生成一个二维码 测试链接 5 通过微信扫描这个二维码跳转
  • 安装Apex库

    在Linux系统下安装Apex库 1 安装流程 按顺序使用如下命令 git clone https github com NVIDIA apex cd apex pip3 install v no cache dir 注意 不能直接使用pi
  • iOS 多线程

    1 怎么用GCD实现多读单写 dispatch barrier async 2 ios系统为我们提供的几种多程序技术各自的特点是怎样的 GCD 实现一些简单的线程同步 子线程的分派 包括一些类似于多读单写 nsoperation 比如adn
  • 2022年最佳的9种逆向工程工具[持续更新]

    逆向是复杂的 然而 软件开发人员经常在面临一项具有挑战性的任务时转向反向工程 增强软件安全性 使软件与第三方组件兼容 维护遗留代码 等等 在本文中 我们将描述我们的软件逆向程序在工作中所依赖的主要工具 并展示如何使用这些工具的实际示例 本文
  • python输入多组测试数据_python ddt数据驱动实例代码分享

    python ddt数据驱动最简实例 在接口自动化测试中 往往一个接口的用例需要考虑 正确的 错误的 异常的 边界值等诸多情况 然后你需要写很多个同样代码 参数不同的用例 如果测试接口很多 不但需要写大量的代码 测试数据和代码柔合在一起 可
  • STM32基于HAL库带FreeRTOS系统的Freemodbus移植

    STM32基于HAL库移植带FreeRTOS系统的Freemodbus移植 移植前提 下载所需源码 可能的win10 IAR设置 从站注意定义寄存器数量大小 效果查询报文 效果回复报文 移植事件 定时器 串口 事件移植 串口移植 定时器移植
  • Electron+React+Antd将网页打包成应用程序完整步骤(electron-builder (搭建热更新) 或 electron-packager(不搭建热更新))

    一 创建React项目 ps 由于写的时候没注意 包安装有的用npm有的用yarn 大家统一用一个就行 尽量不要使用cnpm ps 源码地址 git clone https github com Aug Sunrise electron t
  • 【mysql】mysql表分区、索引的性能测试

    概述 mysql分区表概述 google搜索一下 RANGE COLUMNS partitioning 主要测试mysql分区表的性能 load 500w 条记录 大约在10min左右 batch insert 1 9w条记录 没建立索引
  • 小米升级后开机显示无服务器,小米手机升级后无法开机解决方法

    方法1 刷机 在关机的状态下 进rec中双清 音量上键和开机键按住出来第一个mi画面全部松手 再按住音量加 然后在双清 然后关机音量下键和开机键同时摁住进入fastboot模式 出现米兔修机器人界面 线刷开发版刷机包和教程可以到http w
  • vue3中keep-alive和vue-router的结合使用

    vue3中keep alive和vue router的结合使用 前言 代码 一 为何要使用keep alive 二 vue2中使用keep alive 将 router view 组件包含于 keep alive 即可 三 vue3中使用k
  • 打印二叉树

    摘要 https wylu github io posts 91f36751 本文将使用两种展示方式打印一颗二叉树 效果如下 8 12 14 20 15 13 10 11 9 4 6 7 5 2 3 1 20
  • 50 openEuler搭建PostgreSQL数据库服务器-配置环境

    文章目录 50 openEuler搭建PostgreSQL数据库服务器 配置环境 50 1 关闭防火墙并取消开机自启动 50 2 修改SELINUX为disabled 50 3 创建组和用户 50 4 创建数据盘 50 4 1 方法一 在r
  • 21、同步与异步(三种方法)

    1 同步 按顺序一条一条数据执行 1 同步 按顺序一条一条数据执行 console log 第1条数据 console log 第2条数据 console log 第3条数据 console log 第4条数据 console log 第5
  • flowable工作流简介

    官网地址 https www flowable org Flowable6 3中文教程 https tkjohn github io flowable userguide introduction Flowable Modeler 流程定义
  • 数据结构-树(树基本实现C++)

    树形结构是一种重要的非线性数据结构 其中树和二叉树最为常用 直观看来树是以分支关系定义的层次结构 树形结构是我们平时比较熟悉的 比如文件夹目录 公司组织关系等 在计算机领域也得到广泛的应用 编译程序就是以树来表示源程序的语法结构 二叉树是一