C++实现树 - 01 树的三种表示法

简介: 今天我们开始进入树的学习啦,在学习高级搜索树之前,我们先看看一些简单树的实现,先理解好概念,入门就很快了。另外,本讲将会为大家介绍双亲表示法、孩子表示法和孩子兄弟表示法的代码实现。
写在前面:
今天我们开始进入树的学习啦,在学习高级搜索树之前,我们先看看一些简单树的实现,先理解好概念,入门就很快了。另外,本讲将会为大家介绍双亲表示法、孩子表示法和孩子兄弟表示法的代码实现。

树的定义

之前我们学习的是线性表,也就是线性的存储结构。今天我们开始学习非线性的存储结构,树就是非线性的存储结构,它可以拥有一对多的元素。在学习代码之前,我们先弄明白下面这几个定义:
结点: 使用树结构存储的每⼀个数据元素都被称为“结点”。
在这里插入图片描述

根结点: 有⼀个特殊的结点,这个结点没有前驱,我们将这种结点称之为根结点。
在这里插入图片描述

父结点(双亲结点)、子结点和兄弟结点:
在这里插入图片描述
叶子结点: 如果⼀个结点没有任何子结点,那么此结点就称之为叶子结点。
在这里插入图片描述
结点的度: 结点拥有的子树的个数,就称之为结点的度。
树的度: 在各个结点当中,度的最大值为树的度。
树的深度或者高度: 结点的层次从根结点开始定义起,根为第一层,根的孩子为第二层,依次类推。

双亲表示法

我们可以定义一个结点的结构体,用来保存数据以及它的父节点的下标,并且定义根结点的父节点下标为-1,然后用一个结构体数组来储存所有的结点。
在这里插入图片描述
在这里插入图片描述

代码实现

#include<bits/stdc++.h>
using namespace std;

//树结点的结构体定义
struct tree_node {
    int data;    //数据
    int parent;    //该结点的父亲结点
};

tree_node* Node[5];        //结构体数组,存放结点
int Size;                //当前树的结点个数
int maxSize;            //树的最大结点个数

void init_tree();
void root_insert(int);
void child_insert(int,int);
int find_node(int);

int main() {
    init_tree();
    root_insert(1);
    child_insert(2, 1);
    child_insert(3, 1);
    child_insert(4, 2);
    cout << Node[0]->data << " " << Node[0]->parent<< endl;
    cout << Node[1]->data << " " << Node[1]->parent << endl;
    cout << Node[3]->data << " " << Node[3]->parent << endl;
}

//树的初始化
void init_tree() {
    Size = 0;
    maxSize = 5;
}

//创建根结点
void root_insert(int key) {
    if (Size != 0) {
        cout << "已经创建根节点!" << endl;
        return;
    }
    tree_node* root_node = new tree_node;
    root_node->data = key;
    root_node->parent = -1;
    Node[Size++] = root_node;
}

//孩子结点的插入
void child_insert(int key, int parent) {
    if (Size == 0) {
        cout << "请先创建根节点" << endl;
    }
    else if (Size == maxSize) {
        cout << "节点数已满" << endl;
    }
    else {
        int parent_index = find_node(parent);    //判断是否有该父节点
        if (parent_index == -1) {
            cout << "没有该父节点" << endl;
        }
        else {
            tree_node* child_node = new tree_node;
            child_node->data = key;
            child_node->parent = parent_index;
            Node[Size++] = child_node;
        }
    }
}

//寻找结点
int find_node(int parent) {
    for (int i = 0; i < Size; i++) {
        if (parent == Node[i]->data) {
            return Node[i]->data;
        }
    }
    return -1;
}

优缺点

我们可以通过每个结点parent的值,快速访问其双亲结点。但是如果想知道每个结点的孩子结点,就需要全部遍历一遍,非常麻烦。

孩子表示法

孩子表示法同样用一个数组来保存所有结点,但是每个结点不再是存储父结点的下标,而是定义一个指针,将该结点的所有孩子连起来:
在这里插入图片描述
请添加图片描述

代码实现

#include <bits/stdc++.h>
using namespace std;

//树结点的结构体定义
struct tree_node {
    int data;            //存储数据
    tree_node *next;    //用链表将该结点的所有孩子串起来
};

int Size;                //当前树的结点数量
tree_node *Node[8];        //存储结点的数组

void init_node(int);
void child_insert(int, int);
int find_node(int);

int main() {
    init_node(1);
    child_insert(4, 1);
    child_insert(5, 1);
    child_insert(6, 4);
    child_insert(3, 4);
    child_insert(7, 3);
    for (int i = 0; i < Size; i++) {
        cout << "父节点为" << Node[i]->data;
        tree_node *temp_node = Node[i];
        while (temp_node->next != NULL) {
            temp_node = temp_node->next;
            cout << " 孩子节点为" << temp_node->data;
        }
        cout << endl;
    }
}

//初始化树
void init_node(int key) {
    tree_node *new_node = new tree_node;
    new_node->data = key;
    new_node->next = NULL;
    Size = 0;
    Node[Size] = new_node;
    Size++;
}

//插入孩子结点
void child_insert(int key, int parent) {
    if (Size == 0) {
        cout << "请先创建根节点" << endl;
    } else {
        Node[Size] = new tree_node;
        Node[Size]->data = key;
        Node[Size]->next = NULL;
        Size++;
        int index = find_node(parent);    //寻找父结点在数组中的下标
        if (index == -1) {
            cout << "未找到该父节点" << endl;
        } else {
            tree_node *new_node = new tree_node;
            new_node->data = key;
            new_node->next = Node[index]->next;    //将插入结点的next指向父结点的下个结点
            Node[index]->next = new_node;    //将父结点的next指向该插入结点
        }
    }
}

//找到该结点位置
int find_node(int parent) {
    for (int i = 0; i < Size; i++) {
        if (Node[i]->data == parent) {
            return i;
        }
    }
    return -1;
}

孩子兄弟表示法

孩子表示法需要用数组来存储结点,如果不用数组的话,我们可以在定义结点结构体时,多定义一个指向兄弟的指针,用来连接与该结点同级的结点。
请添加图片描述

全部代码

#include<bits/stdc++.h>
using namespace std;

struct tree_node {
    int data;
    tree_node* child;
    tree_node* sibling;
};

tree_node* root_node = NULL;    //根结点
tree_node* temp_node = NULL;    //临时节点,方便从根节点开始遍历
void root_init(int);
void child_insert(int, int);
void find_node(tree_node*, int);
void show(tree_node* root_node);

int main() {
    root_init(1);
    child_insert(2, 1);
    child_insert(3, 1);
    child_insert(4, 2);
    child_insert(6, 4);
    show(root_node);
}

//遍历树
void show(tree_node* root_node)
{
    if (root_node == NULL)
        return;
    cout << root_node->data << " ";
    show(root_node->sibling);
    show(root_node->child);
}

//根结点初始化
void root_init(int key) {
    root_node = new tree_node;
    root_node->data = key;
    root_node->child = NULL;
    root_node->sibling = NULL;
}

//插入孩子结点
void child_insert(int key, int parent) {
    if (root_node == NULL) {
        cout << "请先创建根节点" << endl;
    }
    else {
        temp_node = NULL;    //用临时指针找到父结点
        find_node(root_node, parent);    //找到父结点的位置
        if (temp_node == NULL) {
            cout << "未找到该父节点" << endl;
        }
        else {
            tree_node* new_node = new tree_node;
            new_node->data = key;
            new_node->sibling = temp_node->child;    //将该结点的兄弟指向父结点的孩子(如果没有就指向空)
            new_node->child = NULL;
            temp_node->child = new_node;    //将父结点的孩子指向该结点
        }
    }
}

//找到该结点的位置
void find_node(tree_node* node, int parent) {
    //如果找到该结点,就将临时指针指向它
    if (node->data == parent) {
        temp_node = node;
    }
    else {
        //先查看结点的兄弟
        if (node->sibling != NULL) {
            find_node(node->sibling, parent);
        }
        //再查看结点的孩子
        if (node->child != NULL) {
            find_node(node->child, parent);
        }
    }
}

如果大家有什么问题的话,欢迎在下方评论区进行讨论哦~

目录
相关文章
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
305 17
|
存储 C++
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
593 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
算法 测试技术 C++
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(下)
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(下)
|
C++ 容器
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(上)
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(上)
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
535 12
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
288 10
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
694 3
|
存储 C++
【C++】AVL树
AVL树是一种自平衡二叉搜索树,由Georgy Adelson-Velsky和Evgenii Landis提出。它通过确保任意节点的两子树高度差不超过1来维持平衡,支持高效插入、删除和查找操作,时间复杂度为O(log n)。AVL树通过四种旋转操作(左旋、右旋、左-右旋、右-左旋)来恢复树的平衡状态,适用于需要频繁进行数据操作的场景。
624 2
|
存储 C++
【C++】AVL树
AVL树是一种自平衡二叉搜索树:它以苏联科学家Georgy Adelson-Velsky和Evgenii Landis的名字命名。
201 2
|
C++ 容器
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
214 5