c语言数据结构-树与二叉树的存储结构

简介: c语言数据结构-树与二叉树的存储结构

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹)

目录

初识树:

初识森林:

初识二叉树:

二叉树与树的区别:

二叉树的几种形态:

满二叉树和完全二叉树:

二叉树的性质:

二叉树的顺序储存结构:

初始化二叉树:

创建二叉树:

获取数的相关数据:

二叉树的链式储存结构:

初始化二叉树:

创建二叉树:


初识树:

1.树是n个结点的有限集

2.结点个数为零的树称为空树 (n=0)

3.任意一颗非空树中(n>0)

       有且仅有一个特定的称为根的结点

       非根的结点可分为互不相交的有限集,每个集合本身又是一棵树,这些树称为根的子树

“发散思维”主题的思维导图 —— 树

主题和子主题 —— 结点

主题 —— 根(根结点)

子主题“抽象”—— 叶(叶结点)

子主题“形”—— 内部结点 (分支结点)

结点的层次:主题“发散思维”为第一层,子主题“跳跃、想象、形”为第二层

                     结点一共有三层,所以树的深度为3

结点的度数:主题“发散思维”有五个子主题,所以结点度数为5

                     结点的最大度数为5,所以树的度为5

初识森林:

森林——删除根节点后不相交的树的集合

有序树——结点各子树从左至右有序,不能互换(左为第一)

无序树——结点各子树可以交换位置

初识二叉树:

二叉树(Binary Tree)是n(n≥0)个结点所构成的集合,它或为空树(n=0);或为非空树,对

于非空树𝑻 :

       有且仅有一个称之为根的结点

       除根以外的其余结点分为两个互不相交的子集𝑻 1 和𝑻 2 ,分别称为𝑻的左子树和右子         树,且𝑻 1 和𝑻 2 本身又都是二叉树

二叉树与树的区别:

二叉树的几种形态:

满二叉树和完全二叉树:

满二叉树:深度为k且含有2的k次方-1个结点的二叉树

特点:

       1.每一层上的结点数都是最大结点数

       2.每一层i的结点数都具有最大值2的(i-1)次方

完全二叉树:深度为k的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中(自上而下从左往右)编号从1至n的结点一一对应。

特点:

       1.叶子结点只可能在层次最大的两层出现

       2.对任一结点,若其右分支下的子孙的最大层次为x, ,则其左分支下的子孙的最大层        次必为x或x+1

       3.最下层的叶结点一定集中在左部连续位置

       4.倒数第二层若有叶结点,一直都在右部连续位置

       5.如果结点度为1,则该结点只有左子树(即不存在只有右子树的情况)

       6.同样结点数的二叉树,完全二叉树的深度最小

二叉树的性质

性质1:在二叉树的第 𝒊 层上至多有2的(i-1)次方个结点

性质2:深度为 𝒊 的二叉树至多有2的i次方-1个结点。

性质3:对于任何一棵二叉树,若2度的结点数有x个。则叶子数y必定为x+1(y=x+1)

性质4:具有𝑛个结点的完全二叉树的深度必为[log2n]+1。([ ]表示向下取整)(2为下标,但是打不出来o(╥﹏╥)o))

性质5:对完全二叉树,若从上至下、从左至右编号,则编号为𝒊的结点,其左孩子编号必为2𝒊 ,其右孩子编号必为2𝒊+1;其双亲的编号必为[𝒊/2]

二叉树的顺序储存结构:

特点:

1 、结点间关系蕴含在其存储位置中

2、 浪费空间,适用于存满二叉树和完全二叉树

初始化二叉树:

/** 一维数组能够存放的最大结点数 */
#define MAX_SIZE 1024
/** 定义顺序树类型 */
typedef char SeqTree[MAX_SIZE];
/** 初始化空二叉树 */
void InitSeqTree(SeqTree tree) 
{
    //将字符数组中的每个元素都设置为空字符
    for (int i = 0; i < MAX_SIZE; i++) 
    {
        tree[i] = '\0';
    }
}

创建二叉树:

/** 创建完全二叉树,i为数组中的下标 */
void CreateSeqTree(SeqTree tree, int i)
{
    char ch;
    ch = getchar();
    if (ch == '^')
    {
        //输入^符号表示结束当前结点的输入
        tree[i] = '\0';
        return;
    }
    tree[i] = ch;
    //某个结点输入完毕后,还需要让用户输入左孩子和右孩子
    printf("左孩子结点:");
    CreateSeqTree(tree, 2 * i + 1); //递归调用
    printf("右孩子结点:");
    CreateSeqTree(tree, 2 * (i + 1));
}

获取数的相关数据:

/** 获取树的根结点元素 */
char GetSeqTreeRoot(SeqTree tree)
{
    return tree[0];
}
/** 获取树的结点总数 */
int GetSeqTreeLength(SeqTree tree) 
{
    int len = 0;
    for (int i = 0; i < MAX_SIZE; i++) 
    {
        if (tree[i] == '\0') 
        {
            continue;
        }
        len++;
    }
    return len;
}
/** 获取树的深度 */
int GetSeqTreeDepth(SeqTree tree)
{
    //性质2:深度为k的二叉树最多有2^k-1个结点
    int depth = 0;      //深度
    int len = GetSeqTreeLength(tree);
    while ((int)pow(2, depth) - 1 < len)
    {
        depth++;
    }
    return depth;
}

二叉树的链式储存结构:

//二叉树的二叉链表存储表示
typedef struct BiNode
{
    /*结点数据域*/
    Element data;
    /*左孩子指针*/
    struct BiNode* lchild;
    /*右孩子指针*/
    struct BiNode* rchild;
}BiTNode, * BiTree;

注意:

在含有n 个结点的二叉链表中有 n+1 个空链域,利用这些空链域可以存储其他有用信息,

从而得到另一种链式存储结构 —— 线索链表

初始化二叉树:

/** 数据元素 */
typedef struct {
  int id;
  char name[NAME_SIZE];
}ElementType;
/** 树结点 */
typedef struct treeNode {
  ElementType data;           //树结点的数据域
  struct treeNode* left;     //左子树
  struct treeNode* right;    //右子树
}TreeNode;
/** 二叉链表 */
typedef struct {
  TreeNode* root;        //二叉链的根结点
  int length;             //二叉链表结点的总数
  int depth;              //二叉链表的深度
  int diameter;           //直径 - 从叶结点到叶结点的最长路径
}BinaryTree;
/** 初始化空二叉树 */
void InitBinaryTree(BinaryTree* tree) {
  tree->root = NULL;
  tree->depth = 0;
  tree->diameter = 0;
  tree->length = 0;
}

创建二叉树:

/** 用来实现结点id的自增长 */
static int id = 0;
/** 构造二叉树 - 外部需要事先对结点分配内存 */
int CreateBinaryTree(TreeNode* root)
{
    //根结点如果为空,就退出创建过程
    if (!root) 
        return 0;
    char inputName[NAME_SIZE];  //用户输入的结点名
    gets_s(inputName);
    //用户输入回车表示结束当前子树的创建
    if (strcmp(inputName, "\0") == 0)
        return 0;
    //创建当前结点
    root->data.id = ++id;
    strcpy(root->data.name, inputName);
    //为输入左右结点做准备 - 为左右结点指针分配内存
    root->left = (TreeNode*)malloc(sizeof(TreeNode));
    root->right = (TreeNode*)malloc(sizeof(TreeNode));
    //分别递归创建左子树和右子树
    printf("左结点:");
    if (CreateBinaryTree(root->left) == 0)
    {
        //不再创建这个结点则销毁结点刚分配的内存
        free(root->left);
        root->left = NULL;
    }
    printf("右结点:");
    if (CreateBinaryTree(root->right) == 0)
    {
        free(root->right);
        root->right = NULL;
    }
    return 1;
}


相关文章
|
2天前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
2天前
|
存储 机器学习/深度学习 C语言
数据结构基础详解(C语言): 树与二叉树的基本类型与存储结构详解
本文介绍了树和二叉树的基本概念及性质。树是由节点组成的层次结构,其中节点的度为其分支数量,树的度为树中最大节点度数。二叉树是一种特殊的树,其节点最多有两个子节点,具有多种性质,如叶子节点数与度为2的节点数之间的关系。此外,还介绍了二叉树的不同形态,包括满二叉树、完全二叉树、二叉排序树和平衡二叉树,并探讨了二叉树的顺序存储和链式存储结构。
|
2天前
|
存储 C语言
数据结构基础详解(C语言): 树与二叉树的应用_哈夫曼树与哈夫曼曼编码_并查集_二叉排序树_平衡二叉树
本文详细介绍了树与二叉树的应用,涵盖哈夫曼树与哈夫曼编码、并查集以及二叉排序树等内容。首先讲解了哈夫曼树的构造方法及其在数据压缩中的应用;接着介绍了并查集的基本概念、存储结构及优化方法;随后探讨了二叉排序树的定义、查找、插入和删除操作;最后阐述了平衡二叉树的概念及其在保证树平衡状态下的插入和删除操作。通过本文,读者可以全面了解树与二叉树在实际问题中的应用技巧和优化策略。
|
2天前
|
存储 算法 C语言
C语言手撕数据结构代码_顺序表_静态存储_动态存储
本文介绍了基于静态和动态存储的顺序表操作实现,涵盖创建、删除、插入、合并、求交集与差集、逆置及循环移动等常见操作。通过详细的C语言代码示例,展示了如何高效地处理顺序表数据结构的各种问题。
|
2天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
2天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
8天前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
8天前
|
C语言
C语言 字符串操作函数
本文档详细介绍了多个常用的字符串操作函数,包括 `strlen`、`strcpy`、`strncpy`、`strcat`、`strncat`、`strcmp`、`strncpy`、`sprintf`、`itoa`、`strchr`、`strspn`、`strcspn`、`strstr` 和 `strtok`。每个函数均提供了语法说明、参数解释、返回值描述及示例代码。此外,还给出了部分函数的自实现版本,帮助读者深入理解其工作原理。通过这些函数,可以轻松地进行字符串长度计算、复制、连接、比较等操作。
|
9天前
|
SQL 关系型数据库 C语言
PostgreSQL SQL扩展 ---- C语言函数(三)
可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)