【数据结构学习记录14】——树与二叉树

零.前言

本来上一章应该还有稀疏矩阵的十字链表存储广义表
但是广义表,更多的是偏向于题目,了解下概念,能计算就行了。广义表从某种程度来说,也算是一棵树。

一.树

1.定义

树是一个n(>=0)个结点的有限集。

2.性质

在任意一颗非空树中:

  1. 有且仅有一个特定的结点叫做根结点root
  2. n>1时,其余结点可以分为m>0个互不相交的有限集T1……Tm。其中,每一个集合的本身又是一颗树,并且被称为根的子树

所以,树的结构定义因该是一个递归的定义。

3.表示方式

除了我们常见的树状图表示法,还有:

  1. 嵌套集合表示法(a)
  2. 广义表表示法(b)
  3. 凹入表示法©
    在这里插入图片描述

4.基本术语

  1. 树的结点包含一个数据元素及若干指向其子树的分支。
  2. 结点拥有的的子树称为结点的度(Degree)
  3. 度为0的结点,称为叶子终端结点
  4. 度不为0的结点,称为分支结点非终端结点
  5. 树的度是树内各结点的度的最大值。
  6. 结点的子树的根,称为该结点的孩子;相应的,该结点是孩子的双亲
  7. 同一个双亲的结点,它们互称为兄弟
  8. 结点的层次从根开始定义,根为第一层,根的孩子在第二层。某结点在n层,那么其子树的根就在n+1层。双亲在同一层的结点叫做堂兄弟
  9. 树中结点的最大层次称为树的深度或高度
  10. 如果将各子树看作从左至右是有次序的,那么称该树是有序树,否则为无序树
  11. 在有序树中,最左边的子树的根称为第一个孩子,最右边的称为最后一个孩子
  12. 森林m>=0棵互不相交的树的集合。对于树中每个结点而言,其子树的集合即位森林。

就逻辑结构而言,任意一棵树是一个二元组Tree=(root,F),其中root是数据元素,称作树的根结点;F是m>=0棵树的森林,F=(T1……Tm),其中Ti=(ri, Fi称做根root的第i棵子树。
在这里插入图片描述

二.二叉树

1.小介绍

如果研究一颗树,那么肯定要从最简单的情况开始研究。当我们每个结点有一个孩子的时候,那么这不就是链表吗?所以我们研究树,一般采用结点有两个孩子的树,叫做二叉树。

2.性质

这里给出一些性质而不给出证明。证明起来也很容易(大概)。

  1. 在二叉树的第i层上至多有2i-1个结点。
  2. 深度为k的二叉树最多有2k-1个结点(k>=1)。
  3. 对于任意一颗二叉树。如果其终端结点数为m,度为2的节点数为n,则m=n+1。
  4. 一颗深度为k,且有2k-1个结点的二叉树称为满二叉树
  5. 对满二叉树进行编号,约定编号从根结点开始,从上至下,从左至右。当一颗树每一个结点都与这种方式的满二叉树的方式一一对应,称之为完全二叉树。它有两个特点: a)叶子结点只可能在层次最大的两层上出现。 b) 对任意结点,其右分支下的子孙最大层次为l,那么其左下分支下子孙的最大层次必为l或l+1.
  6. 具有n个结点的完全二叉树的深度为log2n +1
  7. 如果有一颗有n个结点的完全二叉树,且遵循刚才描述的编号方法,对任意一结点i (1<=i<=n)有:a) 如果i=1,则结点i是二叉树的根,无双亲。如果i>1,则双亲结点的编号是i/2。b)如果2i>n,则结点无左孩子,否则其左孩子是结点2i。c)如果2i+1>n,则结点i无右孩子,否则其右孩子的编号是结点2i+1 。
    在这里插入图片描述

3.二叉树的储存结构

3.1 顺序储存

在这里插入图片描述

顺序存储一般用于完全二叉树。如果用于非完全二叉树,可能会造成大量的空间浪费。

3.2 二叉链表

在这里插入图片描述

由结构来看,一个结点它除了数据域,还有左右指针域。那么最简单的链表就包含了这三个部分。可以理解为单向链表X2。

3.3 三叉链表

在三叉链表里,它比二叉链表多了一个双亲结点的指针。很类似于双向链表。

4.二叉树的遍历

4.1 定义

在二叉树的一些应用中,常常要求在树中查找具有某种特征的点,或者对树中全部结点逐一进行某种处理。这就提出了一个遍历二叉树的问题。按某种搜索路径巡防树中的每个结点,使得每个结点只被访问一次。假设我们有一个1234567的完全二叉树,作为接下来三种遍历的例子。遍历都是基于对非空结点的操作。

4.2 先序遍历

  1. 访问根结点。
  2. 先序遍历左子树。
  3. 先序遍历右子树。
  4. 遍历结果:1245367

4.3 中序遍历

  1. 中序遍历左子树。
  2. 访问根结点。
  3. 中序遍历右子树。
  4. 遍历结果:4251637

4.4 后序遍历

  1. 后序遍历左子树。
  2. 后序遍历右子树。
  3. 访问根结点。
  4. 遍历结果4526731

三.编程原理

1.存储结构

在这篇文章中,我们采用最简单的二叉链表+遍历实现。首先我们还是创建一个数据域的结构体,然后一个结点的结构体,包含两个指向左右儿子结点的指针和一个数据域结构体。

2.存储(先序遍历)

我们通过先序遍历的方法,读入一颗树并构建出这棵二叉树。当然,为了知道每棵树的终端结点,我们需要把终端结点的儿子用虚结点,随意的字符表示。在此约定为#,比如ABC这棵深度为2的树,我们需要输入AB##C##
我们的遍历因该遍历到虚结点,如果字符为#则,该结点值为NULL,如果非#,则结点的数据域存入该字符,并创建儿子结点。

3.遍历输出

如果我们遇到了#结点,那么就停止递归,这是我们的递归边界。
否则就按照某种遍历方式去遍历并输出结点字符即可。

四.代码实现

#include <stdio.h>
#include <stdlib.h>

#define         OK      1
#define         ERROR   0

typedef struct dataType{
    
    
    char data;
}dataType;

typedef struct Btree{
    
    
    dataType node;
    struct Btree *lc;
    struct Btree *rc;
}Btree;

int BtreeCreat(Btree **TreePtr);    // 通过先序遍历的方式创建二叉树
int BtreeShowDLR(Btree *root);      // 三种遍历
int BtreeShowLDR(Btree *root);
int BtreeShowLRD(Btree *root);

int main()
{
    
    
    Btree *root = NULL;
    printf("\nplase input the tree as DLR:\n");
    BtreeCreat(&root);
    printf("\nDLR is: ");
    BtreeShowDLR(root);
    printf("\nLDR is: ");
    BtreeShowLDR(root);
    printf("\nLRD is: ");
    BtreeShowLRD(root);
    return 0;
}

int BtreeCreat(Btree **TreePtr)
{
    
    
    // 因为会对传入的结点的地址进行修改,所以需要用到二重指针
    char ch;
    ch = getchar();  //读入字符
    if (ch == '#')
    {
    
    
        *TreePtr = NULL;    // 如果这个字符是#,则将这个结点的地址赋为空
    }
    else    //非空
    {
    
    
        *TreePtr = (Btree*)malloc(sizeof(Btree));   // 创建结点
        if (!*TreePtr) return ERROR;        // 创建失败退出
        (*TreePtr)->node.data = ch;         // 将结点的值赋值为字符
        BtreeCreat(&((*TreePtr)->lc));      // 遍历创建左结点
        BtreeCreat(&((*TreePtr)->rc));      // 遍历创建右结点
    }

    return OK;
}

int BtreeShowDLR(Btree *root)   // 因为遍历不会对结点地址进行改变,所以不需要二重指针
{
    
    
    if (root != NULL)   // 如果结点不为空,说明它有值
    {
    
    
        printf("%c", root->node.data);  // 输出根结点
        BtreeShowDLR(root->lc);         // 遍历左结点
        BtreeShowDLR(root->rc);         // 遍历右结点
    }
    // 为空没有任何操作
    return OK;
}

int BtreeShowLDR(Btree *root)
{
    
    
    if (root != NULL)   // 遍历顺序不一样
    {
    
    
        BtreeShowLDR(root->lc);
        printf("%c", root->node.data);
        BtreeShowLDR(root->rc);
    }
    return OK;
}

int BtreeShowLRD(Btree *root)
{
    
    
    if (root != NULL)   // 遍历顺序不一样
    {
    
    
        BtreeShowLRD(root->lc);
        BtreeShowLRD(root->rc);
        printf("%c", root->node.data);
    }
    return OK;
}

猜你喜欢

转载自blog.csdn.net/u011017694/article/details/109859754