C语言数据结构系列之树的概念结构和常见表示方法

C语言数据结构系列之树的概念结构和常见表示方法

树是一种非线性数据结构,它由若干个节点构成,节点之间通过边来连接,具有层次关系。

树的基本概念和术语

  • 节点:树中的元素,它可以包含一个数据元素或多个数据元素,一个节点也可以称为一个分支节点。
  • 根节点:树的最上层节点,它没有父节点。
  • 叶子节点:没有子节点的节点称为叶子节点。
  • 父节点和子节点:父节点是某个节点的上一级节点,子节点是某个节点的下一级节点。
  • 兄弟节点:具有同一父节点的节点称为兄弟节点。
  • 子树:一个节点和它的子节点构成的树称为子树。

树的表示方法

双亲表示法

双亲表示法是通过每个节点存储它的父节点来表示树的关系。

#define MAX_TREE_SIZE 100
typedef struct{
    ElemType data; // 数据元素
    int parent; // 父节点位置
}PTNode;
typedef struct{
    PTNode nodes[MAX_TREE_SIZE]; // 双亲节点数组
    int r; // 树根位置
    int n; // 节点数目
}PTree;

使用双亲表示法时,需要遍历一次树找到每个节点的父节点并记录,这个过程会导致时间开销较大。

孩子表示法

孩子表示法是通过每个节点存储它的一个子节点指针,来表示树的关系。

#define MAX_TREE_SIZE 100
typedef struct CTNode{
    int child; // 子节点在数组中的位置
    struct CTNode *next; // 指向下一个子节点的指针
} *ChildPtr;

typedef struct{
    ElemType data; // 数据元素
    ChildPtr firstchild; // 子节点指针
}CTBox;

typedef struct{
    CTBox nodes[MAX_TREE_SIZE]; // 子节点数组
    int r; // 树根的位置
    int n; // 节点个数
}CTree;

使用孩子表示法时,需要为每个节点创造一个子节点指针,这可能会引起大量的空间浪费。另外,孩子表示法也需要对树进行遍历来找到每个节点的子节点。

孩子兄弟表示法

孩子兄弟表示法是通过每个节点存储它的第一个子节点和下一个兄弟节点,来表示树的关系。

typedef struct CSNode{
    ElemType data;
    struct CSNode *firstchild; // 指向第一个子节点的指针
    struct CSNode *nextsibling; // 指向下一个兄弟节点的指针
}CSNode, *CSTree;

使用孩子兄弟表示法时,一个节点可以快速地访问其第一个子节点和下一个兄弟节点,但也需要遍历树以找到某个节点的后代或兄弟节点。

树的遍历

树的遍历就是按照某种规则依次访问树中所有节点的过程。

先序遍历

先序遍历就是先访问根节点,再按照先序遍历的方式遍历左子树和右子树。在先序遍历中,根节点总是在第一个被访问。

void PreOrderTraverse(CSTree T){
    if(T){ // 树不为空
        visit(T); // 访问根节点
        PreOrderTraverse(T -> firstchild); // 对左子树进行遍历
        PreOrderTraverse(T -> nextsibling); // 对右子树进行遍历
    }
}

中序遍历

中序遍历就是先按中序遍历的方式遍历左子树,然后访问根节点,最后按照中序遍历的方式遍历右子树。在中序遍历中,根节点总是在左右子树之间被访问。

void InOrderTraverse(CSTree T){
    if(T){ // 树不为空
        InOrderTraverse(T -> firstchild); // 对左子树进行遍历
        visit(T); // 访问根节点
        InOrderTraverse(T -> nextsibling); // 对右子树进行遍历
    }
}

后序遍历

后序遍历就是先按照后序遍历的方式遍历左子树和右子树,然后访问根节点。在后序遍历中,根节点总是在左右子树被访问之后才被访问。

void PostOrderTraverse(CSTree T){
    if(T){ // 树不为空
        PostOrderTraverse(T -> firstchild); // 对左子树进行遍历
        PostOrderTraverse(T -> nextsibling); // 对右子树进行遍历
        visit(T); // 访问根节点
    }
}

示例一

以下是使用孩子表示法创建一棵具有5个节点的树,并通过先序遍历、中序遍历和后序遍历遍历树的过程:

CTree create_tree(){
    CTree T;
    T.n = 5;
    T.r = 0;
    T.nodes[0].data = 1;
    T.nodes[1].data = 2;
    T.nodes[2].data = 3;
    T.nodes[3].data = 4;
    T.nodes[4].data = 5;
    T.nodes[0].firstchild = &T.nodes[1]; // 节点1的第一个子节点是节点2
    T.nodes[1].firstchild = &T.nodes[2]; // 节点2的第一个子节点是节点3
    T.nodes[2].firstchild = NULL; // 节点3没有子节点
    T.nodes[3].firstchild = &T.nodes[4]; // 节点4的第一个子节点是节点5
    T.nodes[4].firstchild = NULL; // 节点5没有子节点
    return T;
}

void visit(CTree T){
    printf("%d ", T.data);
}

void traverse(){
    CTree T = create_tree();
    printf("先序遍历:");
    PreOrderTraverse(T.nodes[0].firstchild);
    printf("\n中序遍历:");
    InOrderTraverse(T.nodes[0].firstchild);
    printf("\n后序遍历:");
    PostOrderTraverse(T.nodes[0].firstchild);
}

这个示例创建了一棵具有5个节点的树,并分别使用先序遍历、中序遍历和后序遍历遍历树中的所有节点。

示例二

以下是使用孩子兄弟表示法创建一棵具有6个节点的二叉树,并通过先序遍历、中序遍历和后序遍历遍历二叉树的过程:

CSTree create_binary_tree(){
    CSTree T;
    T = (CSTree)malloc(sizeof(CSNode));
    T -> data = 1;
    T -> firstchild = (CSTree)malloc(sizeof(CSNode));
    T -> firstchild -> data = 2;
    T -> firstchild -> firstchild = (CSTree)malloc(sizeof(CSNode));
    T -> firstchild -> firstchild -> data = 4;
    T -> firstchild -> firstchild -> firstchild = NULL;
    T -> firstchild -> firstchild -> nextsibling = (CSTree)malloc(sizeof(CSNode));
    T -> firstchild -> firstchild -> nextsibling -> data = 5;
    T -> firstchild -> firstchild -> nextsibling -> firstchild = NULL;
    T -> firstchild -> firstchild -> nextsibling -> nextsibling = NULL;
    T -> firstchild -> nextsibling = (CSTree)malloc(sizeof(CSNode));
    T -> firstchild -> nextsibling -> data = 3;
    T -> firstchild -> nextsibling -> firstchild = NULL;
    T -> firstchild -> nextsibling -> nextsibling = NULL;
    return T;
}

void visit(CSTree T){
    printf("%d ", T -> data);
}

void traverse(){
    CSTree T = create_binary_tree();
    printf("先序遍历:");
    PreOrderTraverse(T);
    printf("\n中序遍历:");
    InOrderTraverse(T);
    printf("\n后序遍历:");
    PostOrderTraverse(T);
}

这个示例创建了一棵具有6个节点的二叉树,并分别使用先序遍历、中序遍历和后序遍历遍历树中的所有节点。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言数据结构系列之树的概念结构和常见表示方法 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • 【ACM算法竞赛日常训练】DAY3题解与分析【旅游】【tokitsukaze and Soldier】

    DAY3共2题: 旅游 tokitsukaze and Soldier ? 作者:Eriktse? 简介:19岁,211计算机在读,现役ACM银牌选手?力争以通俗易懂的方式讲解算法!❤️欢迎关注我,一起交流C++/Python算法。(优质好文持续更新中……)?? 原文链接(阅读原文获得更好阅读体验): 旅游 题目传送门:https://ac.nowcoder…

    算法与数据结构 2023年4月18日
    00
  • 【ACM组合数学 | 错排公式】写信

    题目链接:https://ac.nowcoder.com/acm/contest/54484/B 题意很简单,但是数据范围偏大。 错排公式 首先来推导一下错排公式: \[D(n) = n!\sum_{k=0}^{n}\frac{(-1)^k}{k!} \] 设一个函数: \[S_i表示一个排列中p_i = i的方案数 \] 那么我们可以知道: \[D(n) …

    算法与数据结构 2023年4月17日
    00
  • C语言数据结构之单链表的查找和建立

    C语言数据结构之单链表的查找和建立 什么是单链表? 单链表是一种常见的数据结构,是由若干个节点(Node)组成的链式结构,每个节点存储着链表中的元素和指向下一个节点的指针。 单链表的优点是插入、删除元素简单,但是查找元素比较困难。 在C语言中,我们可以使用结构体来定义一个节点: struct ListNode { int val; struct ListNo…

    数据结构 2023年5月17日
    00
  • PHP 数据结构 算法 三元组 Triplet

    PHP 数据结构 算法 三元组 Triplet 什么是三元组 Triplet 三元组 Triplet 是指由三个数据分别确定一个元素的数据类型。 在 PHP 中可以用一个数组来实现三元组,数组下标表示元素的序号,数组中储存的则是元素的值,共有三个元素。 例如一个三元组 (a, b, c),可以用 PHP 数组表示为 $triplet = array(a, b…

    数据结构 2023年5月17日
    00
  • Java数据结构常见几大排序梳理

    Java数据结构常见几大排序梳理 在Java中,数据排序是非常常见的操作。下面我们详细讲解一下Java数据结构常见几大排序的梳理。 常见几大排序 Java数据结构中常见几种排序算法包括: 冒泡排序(Bubble Sort) 快速排序(Quick Sort) 插入排序(Insertion Sort) 选择排序(Selection Sort) 希尔排序(Shel…

    数据结构 2023年5月17日
    00
  • Python 树表查找(二叉排序树、平衡二叉树)

    下面是 Python 树表查找(二叉排序树、平衡二叉树)的完整攻略: 什么是树表查找 树表查找是一种数据结构,用于在数据集合中快速查找、插入和删除数据。树表查找的基本思想是利用特定的树形结构,在不断比较和移动中找到目标数据。常见的树表查找有二叉排序树和平衡二叉树。 二叉排序树(Binary Search Tree) 二叉排序树是一种特殊的二叉树结构,它满足以…

    数据结构 2023年5月17日
    00
  • 带你了解Java数据结构和算法之2-3-4树

    带你了解Java数据结构和算法之2-3-4树 1. 什么是2-3-4树 2-3-4树是一种自平衡二叉查找树,也叫B树的一种,它可以保持树的平衡,使得每个节点的左右子树高度差最多为1。在2-3-4树中,每个节点可以包含2个、3个或4个子节点,这也是其名称的来源。2-3-4树是B树的特殊形式,通常用于内存储存结构。 2. 2-3-4树的特点 2-3-4树的特点如…

    数据结构 2023年5月17日
    00
  • 线段树好题! P2824 [HEOI2016/TJOI2016]排序 题解

    题目传送门 前言 线段树好题!!!!咕咕了挺久的一道题目,很早之前就想写了,今天终于找了个时间A掉了。 题意 给定一个 \(1\) 到 \(n\) 的排列,有 \(m\) 次操作,分两种类型。1.0 l r表示将下标在 \([l, r]\) 区间中的数升序排序。2.1 l r表示将下标在 \([l, r]\) 区间中的数降序排序。给定一个数 \(q\) 询问…

    算法与数据结构 2023年4月17日
    00
合作推广
合作推广
分享本页
返回顶部