数据结构-难点突破(C++实现树的双亲表示法,孩子表示法,孩子兄弟表示法(树转化为二叉树))

news2025/1/21 6:02:56

文章目录

  • 1. 树的双亲表示法
  • 2. 孩子表示法
  • 3. 孩子兄弟表示法(树转化为二叉树)

普通树的存储一半采用三种方式:

  1. 双亲表示法;
  2. 孩子表示法;
  3. 孩子兄弟表示法;

1. 树的双亲表示法

思路和图片来源
在这里插入图片描述

采用双亲表示法后的图为:
在这里插入图片描述

//顺序存储三叉树的每个节点,数组的值为父节点的下标

//这里练习使用双亲表示法构建层序构建三叉树

#include <vector>
#include <math.h>
#include <assert.h>

template <class T>
struct Elem
{
    //每个树节点有两个数据,当前的值个父亲节点的位置
    T data;
    int parent;
    Elem(T data, int parent) : data(data), parent(parent) {}
};

template <class T>
class Tree
{
    typedef Elem<T> ElemNode;

public:
    Tree(const std::vector<T> &vet)
    {
        int parent = 0; //第一个父节点
        int times = 0;  //记录父节点有几个孩子
        int level = 0;  //记录这是二叉树的第几次
        int pos = 0;    //记录访问到数组的第几个位置
        while (pos < vet.size())
        {
            if (nodes.empty())
            {
                //第一个节点
                nodes.push_back(ElemNode(vet[pos++], -1));
            }
            else
            {
                //计算这层最多可以有几个节点
                for (int j = 0; j < pow(3, level - 1); j++)
                {
                    nodes.push_back(ElemNode(vet[pos++], parent));
                    times++;
                    if (times == 3)
                    {
                        //这里实现的三叉树,所以当这个节点有三个子节点后父亲节点+1
                        parent++;
                        times = 0;
                    }
                }
            }
            level++;
        }
    }

    //获取某个节点的父节点
    ElemNode getParent(const T &num)
    {
        //在数组中查找这个元素
        size_t pos = findElem(num);
        assert(pos < nodes.size());
        return nodes[nodes[pos].parent];
    }
    std::vector<ElemNode> nodes;

private:
    size_t findElem(T num)
    {
        for (int i = 0; i < nodes.size(); i++)
        {
            if (nodes[i].data == num)
            {
                return i;
            }
        }
        return -1;
    }
};
#include "Tree.h"
#include <iostream>
using namespace std;

int main(int argc, char const *argv[])
{
    Tree<int> tree({1, 2, 3, 4, 5, 6, 7, 8, 9});
    cout << tree.getParent(9).data;
    return 0;
}

在这里插入图片描述

2. 孩子表示法

孩子表示法存储普通树采用的是 “顺序表+链表” 的组合结构。

其存储过程是:从树的根节点开始,使用顺序表依次存储树中各个节点。需要注意,与双亲表示法不同的是,孩子表示法会给各个节点配备一个链表,用于存储各节点的孩子节点位于顺序表中的位置。

如果节点没有孩子节点(叶子节点),则该节点的链表为空链表。

在这里插入图片描述

/**
孩子表示法存储普通树采用的是 "顺序表+链表" 的组合结构,其存储过程是:从树的根节点开始,使用顺序表依次存储树中各个节点。
需要注意,与双亲表示法不同的是,孩子表示法会给各个节点配备一个链表,用于存储各节点的孩子节点位于顺序表中的位置。
如果节点没有孩子节点(叶子节点),则该节点的链表为空链表。

链表存储的值不是数据本身,而是数据在数组中的位置
 *
 */

#include <vector>
#include <iostream>
#include <stdio.h>
#include <assert.h>

struct listNode
{
    int pos;
    listNode *next = nullptr;
    listNode(int _pos) : pos(_pos) {}
};

struct ChildList
{
    int data;
    listNode *node = nullptr;
    ChildList(int _data) : data(_data){};
};

//让用户构建树
class Tree
{
    std::vector<ChildList> node;

public:
    Tree()
    {
        std::cout << "输入树的节点个数" << std::endl;
        int capacity = 0;
        std::cin >> capacity;
        int size = 0;
        while (size < capacity)
        {
            std::cout << "输入数组下标" << size << "的值";
            int data = 0;
            std::cin >> data;
            ChildList list(data);
            std::cout << "输入这个节点有几个孩子" << std::endl;
            int child = 0;
            std::cin >> child;
            listNode *next = nullptr;
            for (int i = 1; i <= child; i++)
            {
                printf("输入第%d个孩子节点在数组的下标", i);
                int pos = 0;
                std::cin >> pos;
                listNode *node = new listNode(pos);
                if (list.node == nullptr)
                {
                    list.node = node;
                    next = node;
                }
                else
                {
                    next->next = node;
                    next = next->next;
                }
            }
            node.push_back(list);
            size++;
        }
    }

    //尽可能的表示树结构
    void DisPlay()
    {
        //打印树结构
        std::vector<std::vector<int>> msg; //保存树的结构
        //读取树每层的结构
        std::vector<int> pos(1, 0); //存放下一层节点的位置
        std::vector<int> prev;

        int max = 0; //记录层最大节点个数
        for (int i = 0; i < this->node.size(); i++)
        {
            std::vector<int> data;
            for (int i = 0; i < pos.size(); i++)
            {
                data.push_back(this->node[pos[i]].data);
            }
            max = data.size() > max ? data.size() : max;
            msg.push_back(data);
            //修改存放下一层pos数组
            prev = pos;
            pos.clear();
            for (int i = 0; i < prev.size(); i++)
            {
                listNode *node = this->node[prev[i]].node;
                while (node != nullptr)
                {
                    pos.push_back(node->pos);
                    node = node->next;
                }
            }
        }

        //如果层节点小于max先打印
        for (int i = 0; i < msg.size(); i++)
        {
            int size = msg[i].size();
            if (size < max)
            {
                //打印空格
                for (int j = 0; j <= (max - size) / 2; j++)
                {
                    std::cout << " ";
                }
            }
            for (int k = 0; k < size; k++)
            {
                std::cout << msg[i][k] << " ";
            }
            std::cout << "\n";
        }
    }

    //查找树节点值data的子节点
    void findChild(int data)
    {
        int pos = findPos(data);
        if (pos == -1)
        {
            printf("树中没有此元素\n");
        }
        else
        {
            listNode *list = this->node[pos].node;
            printf("%d的子节点值为:\n", data);
            while (list != nullptr)
            {
                std::cout << this->node[list->pos].data << " ";
                list = list->next;
            }
            std::cout << "\n";
        }
    }

private:
    int findPos(int data)
    {
        for (int i = 0; i < this->node.size(); i++)
        {
            if (this->node[i].data == data)
            {
                return i;
            }
        }
        return -1;
    }
};
#include "Tree.h"

using namespace std;

int main(int argc, char const *argv[])
{
    Tree tree;
    tree.DisPlay();
    tree.findChild(0);
    return 0;
}

在这里插入图片描述

3. 孩子兄弟表示法(树转化为二叉树)

所谓孩子兄弟表示法,指的是用将整棵树用二叉链表存储起来.
具体实现方案是:从树的根节点开始,依次存储各个结点的孩子结点和兄弟结点。

在二叉链表中,各个结点包含三部分内容:

  1. 节点的值;
  2. 指向孩子结点的指针;
  3. 指向兄弟结点的指针;

通过孩子兄弟表示法,任意一棵普通树都可以相应转化为一棵二叉树,它们是一一对应的。

在这里插入图片描述
在这里插入图片描述

/**
所谓孩子兄弟表示法,指的是用将整棵树用二叉链表存储起来.
具体实现方案是:从树的根节点开始,依次存储各个结点的孩子结点和兄弟结点。

在二叉链表中,各个结点包含三部分内容:
1. 节点的值;
2. 指向孩子结点的指针;
3. 指向兄弟结点的指针;

通过孩子兄弟表示法,任意一棵普通树都可以相应转化为一棵二叉树,它们是一一对应的。

*
*/

#include <iostream>
#include <vector>

struct TreeNode
{
    char val;
    TreeNode *child;
    TreeNode *brother;
    TreeNode(int _val) : val(_val), child(nullptr), brother(nullptr) {}
};

class Tree
{
public:
    TreeNode *root;
    Tree()
    {
        std::vector<TreeNode *> cur_roots; //保存这一层的节点
        std::cout << "请输入根节点的值" << std::endl;
        char val = 0;
        std::cin >> val;
        root = new TreeNode(val);
        cur_roots.push_back(root);
        while (cur_roots.size() != 0)
        {
            std::vector<TreeNode *> next_roots;
            for (int i = 0; i < cur_roots.size(); i++)
            {
                TreeNode *root = cur_roots[i];
                std::cout << "输入" << root->val << "孩子节点个数" << std::endl;
                int size = 0;
                std::cin >> size;
                TreeNode *tail = nullptr;
                for (int j = 0; j < size; j++)
                {
                    std::cout << "输入第" << j + 1 << "个孩子节点的值" << std::endl;
                    char data = 0;
                    std::cin >> data;
                    if (j == 0)
                    {
                        //第一个节点放到左链表上,代表孩子
                        root->child = new TreeNode(data);
                        tail = root->child;
                    }
                    else
                    {
                        tail->brother = new TreeNode(data);
                        tail = tail->brother;
                    }
                    next_roots.push_back(tail);
                }
            }
            cur_roots = next_roots;
        }
    }

    //最后构造完树后成二叉树,可以直接使用前序遍历
    void _PreDisplay(TreeNode *node)
    {
        if (node == nullptr)
            return;
        std::cout << node->val << " ";
        _PreDisplay(node->child);
        _PreDisplay(node->brother);
    }
    void PreDisplay()
    {
        _PreDisplay(root);
    }
};
#include "Tree.h"

int main(int argc, char const *argv[])
{
    Tree tree;
    tree.PreDisplay();
    return 0;
}

在这里插入图片描述

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

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

相关文章

智慧停车解决方案-最新全套文件

智慧停车解决方案-最新全套文件一、建设背景痛点分析二、建设思路准确、安全、可靠、及时性原则统一规划、分布实施保护以往投资、整合现有资源资源共享和整体性、统一性原则可扩展性原则三、建设方案四、获取 - 智慧停车全套最新解决方案合集一、建设背景 痛点分析 随着经济…

stm32cubemx hal学习记录:FreeRTOS事件

一、事件 事件是一种实现任务间通信的机制&#xff0c;主要用于实现多任务间的同步&#xff0c;但事件通信只能是事件类型的通信&#xff0c;无数据传输。与信号量不同的是&#xff0c;它可以实现一对多&#xff0c;多对多的同步。即一个任务可以等待多个事件的发生&#xff1a…

C语言源代码系列-管理系统之机房机位预定系统

往期文章分享点击跳转>《导航贴》- Unity手册&#xff0c;系统实战学习点击跳转>《导航贴》- Android手册&#xff0c;重温移动开发 &#x1f449;关于作者 众所周知&#xff0c;人生是一个漫长的流程&#xff0c;不断克服困难&#xff0c;不断反思前进的过程。在这个过…

华为电量分段图表实现过程

以前一直是改的MPAndroidChart&#xff0c;但最近看到华为手机的电池图表发现一旦设计不符合常规图表逻辑实现起来就很困难&#xff0c; 考虑过path相减(areaPath.op(-,- Path.Op.DIFFERENCE))、图像混合&#xff08;paint.setXfermode&#xff09;、裁剪区域&#xff08;clipR…

学生HTML个人网页作业作品下载 动漫主题网页设计制作 大学生个人网站作业模板 dreamweaver简单个人网页制作

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 ⚽精彩专栏推荐&#x1…

vue3面试题

文章目录一、vue3有了解过吗&#xff1f;能说说跟vue2的区别吗&#xff1f;1.vue3介绍2.vue3的新特性&#xff1a;2.1速度更快2.2体积更小2.3更易维护2.4更好的Typescript支持2.5编译器重写2.6更接近原生2.7更易使用3.vue3新增特性framentsTeleportcreateRenderercomposition A…

JVM区域划分

概述 JVM在运行代码时&#xff0c;他使用多块内存空间&#xff0c;不同的内存空间用来放不同的数据&#xff0c;然后配合代码流程&#xff0c;让系统运行起来。 存放类加载信息 举个最简单的例子&#xff0c;比如现在知道了JVM会加载类到内存里来供后续运行&#xff0c;所以…

java后端web前端10套项目开发案例源码,毕设,期末作业

项目包括 基础的html,css,js,jquery期末作业项目,毕设 1.古风诗意主题的背单词项目 2.模仿考虫软件实现的考拉背单词网页 3.模仿不背单词软件实现的背单词项目 html,css,js,jquery,瀑布流&#xff0c;懒加载的前端毕设 仿京东网站实现的前端平台 java基础作业 1.纯后端学生管…

初识Nodejs -- nodejs简介

视频链接&#xff1a;黑马程序员Node.js全套入门教程 文章目录1. 初识Nodejs1.1 思考为什么JavaScript可以在浏览器中被执行为什么JavaScript可以操作DOM和BOM&#xff1f;浏览器中的JavaScript运行环境1.2 Nodejs简介1.2.1 Nodejs中的JavaScript运行环境1.2.2 Node.js可以做什…

元数据管理-解决方案调研三:元数据管理解决方案——开源解决方案

开源解决方案 3.1、Apache Atlas Atlas 是一组可扩展和可扩展的核心基础治理服务——使企业能够有效和高效地满足其在 Hadoop 中的合规性要求&#xff0c;并允许与整个企业数据生态系统集成。 Apache Atlas 为组织提供开放的元数据管理和治理功能&#xff0c;以构建其数据资产…

没有公网IP,怎么远程访问ERP/NAS?

当前&#xff0c;国内疫情形势不容乐观&#xff0c;企业的经营发展再一次面临巨大挑战。虽然“远程办公”早已不是新鲜词&#xff0c;但依然有大量企业没有做好随时切换到远程办公的准备。如遇疫情风险&#xff0c;企业运营很容易陷入瘫痪。 企业一般在内网部署服务器、视频监…

2分钟看懂OA与ERP

ERP (Enterprise Resource Planning)即企业资源计划&#xff0c;最早是一种供应链的管理思想。它汇合了商贸代理行业的各项特点&#xff0c;旨在协调企业各管理部门围绕市场导向&#xff0c;更加灵活或柔性地开展业务活动&#xff0c;实时地响应市场需求。 从功能来看&#xf…

AVL树左旋转算法思路与图解

AVL树左旋转算法思路与图解 对于数列{4, 3, 6, 5, 7, 8}, 当我们插入8的时候, rightHeight() - leftHeight() > 1成立(也就是当前AVL树中的根节点的BF(平衡因子)> 1了), 此时这个AVL树已经不再是平衡的了, 也就是已经不是一个AVL树了, 所以我们要经过处理之后让其重新平…

【C】输入一行字符,分别统计出其中英文字母、数字、空格和其他字符的个数

C语言没有字符串形式&#xff0c;所以可以用 char[] 数组来代替&#xff0c;但需要指定分配空间&#xff0c;所以可以采用单字符读取的形式&#xff1a; #include<stdio.h> int main(){char ch;int space0,number0,character0,other0;chgetchar(); …

WSO2安装使用的全过程详解

WSO2安装使用的全过程详解1. Install1.1 Docker Install 1.2 Uninstall2. User Guide3. Big Data -- Postgres4. Awakening1. Install 1.1 Docker Install 显示镜像 docker images/docker image ls拉去镜像/版本 docker pull wso2/wso2am 1.2 Uninstall 查看正在运行的容器 d…

流体动力润滑(轴承油膜承载机理)

目录 1.流体动力润滑 2.流体动力润滑的应用&#xff1a;楔形效应承载机理 3.承载量的计算 4.轴承动压油膜 1.流体动力润滑 定义&#xff1a;两个相对运动物体的摩擦表面&#xff0c;用于借助相对速度而产生的黏性流体膜将两摩擦面完全隔离开&#xff0c;由流体膜产生的压力…

那些下载不了的视频,Python只用1行代码就能直接下载

前言 现在有很多网站都并不支持直接下载的&#xff0c;例如我们常去的B站里面的视频&#xff0c;在页面是没有下载按钮的&#xff0c;还有的视频需要我们下载客户端才能下载…虽然这并不能拦住多少人&#xff0c;有些聪明的小伙伴就会去下载一些第三方软件去下载&#xff0c;比…

apache-atlas-hbase-bridge-源码分析

元数据类型 Hbase元数据类型, 包括命令空间、表、列族、列 public enum HBaseDataTypes {// ClassesHBASE_NAMESPACE,HBASE_TABLE,HBASE_COLUMN_FAMILY,HBASE_COLUMN;public String getName() {return name().toLowerCase();} }Hbase元数据采集实现 1&#xff09;批量采集HBa…

MyBatis基于XML的使用——缓存

1、介绍 MyBatis 内置了一个强大的事务性查询缓存机制&#xff0c;它可以非常方便地 配置和定制。 为了使它更加强大而且易于配置&#xff0c;我们对 MyBatis 3 中的缓存实现进行了许多改进。 默认情况下&#xff0c;只启用了本地的会话缓存&#xff0c;它仅仅对一个会话中的数…

【Java】异常处理

异常本质上是程序上的错误&#xff0c;包括程序逻辑错误和系统错误。比如使用空的引用、数组下标越界、内存溢出错误等. 错误在我们编写程序的过程中会经常发生&#xff0c;包括编译期间和运行期间的错误&#xff0c;在编译期间出现的错误有编译器帮助我们一起修正&#xff0c;…