树(上)

定义

树的定义: T = { D , R } T=\{D,R\} 。D是包含n个节点的有限集合( n 0 n \geq0 )。当n=0时为空树,否则关系R满足以下条件:

  • 有且仅有一个节点 d 0 D d_{0}\in D ,它对于关系R来说没有前驱节点,节点 d 0 d_0 称作树的根节点
  • 除根节点外,每个节点有且仅有一个前驱节点
  • D D 中每个节点可以有零个或多个节点后继

树的递归定义:树是由 n ( n 0 ) n(n\geq0) 个节点组成的有限集合(记为 T T )。其中:

  • 如果 n = 0 n=0 ,它是一棵空树,这是树的特例
  • 如果 n > 0 n>0 ,其中存在一个唯一节点作为树的根节点(root),其余节点可分为 m ( m 0 ) m(m\geq0) 个互不相交的有限子集 T 1 T 2 T m T_1、T_2、T_m ,而每个子集本身又是一棵树,称为根节点root的子树

树的基本术语

1.节点的度与树的度:

  • 树中一个节点的子树的个数称为该节点的度
  • 树中各节点的最大值称为树的度,通常将度为$m$的树称为m次数或者$m$叉树

2.分支节点与叶节点:

  • 度不为0的节点称为非终端节点,又叫分支节点
  • 度为0的节点称为终端节点或叶节点(或叶子节点)
  • 度为1的节点称为单分支节点;度为2的节点称为双分支节点,以此类推

3.路径与路径长度:

  • 两个节点 d i d_{i} d j d_{j} 的节点序列称为路径。
  • 路径的长度等于路径通过所有的节点数目减1
    在这里插入图片描述

4.子节点、父节点、兄弟节点:

  • 每个节点的后继被称为子节点
  • 该节点被称为子节点的父节点
  • 具有相同父节点的子节点互为兄弟节点

5.子孙节点和祖先节点:

  • 一个节点的所有子树节点称为该节点的子孙节点
  • 从根节点到达一个节点的路径上经过的所有节点被称作该节点的祖先节点

6.节点的层次和树的高度:

  • 层次:根节点为第一层,它的子节点为第二层,以此类推
  • 树中节点的最大层次称为树的高度

7.有序树和无序树:

  • 若树中各节点的子树是按照一定的次序从左向右安排的,且相对次序不能随意变换,则称为有序树
  • 否则称为无序树

8.森林:

  • n n 个互不相交的树的集合称为森林
  • 只要把树的根节点删去就成了森林
  • 只要给 n n 棵独立的树加上一个节点,并把 n n 棵树作为该节点的子树,

树的性质

性质1:树中的节点数等于等于所有节点的度数加1

性质2:度为m的树中第i层上至多有 m i 1 m^{i-1} 个节点( i 1 i\geq1

性质3:高度为h的m次树至多有 m h 1 m 1 \frac{m^{h}-1}{m-1} 个节点

性质4:具有n个节点的m次树的最小高度为 l o g m ( n ( m 1 ) + 1 ) log_m(n(m-1)+1)

树的基本运算

树的基本运算分三大类:

  • 查找满足某种特定关系的节点,如查找当前节点的父节点等
  • 插入或删除某节点,如在树的当前节点上插入一个新节点或删除当前节点的第i个子节点等
  • 遍历树中每个节点

遍历:

  • 先根遍历:若树不为空,则先访问根节点,然后依次先根遍历各棵子树
  • 后根遍历:若树不为空,则先依次后根遍历各棵子树,然后访问根节点
  • 层次遍历:若树不为空,则自上而下、从左至右访问树中的每个节点

注意: 先根和后根遍历算法都是递归的

树的存储结构

  • 双亲存储结构
typedef struct{
    ElemType data;  //节点的值
    int parent;     //指向双亲的位置
}PTree[MaxSize];

在这里插入图片描述

typedef struct node{
    ElemType data;              //节点的值
    struct node* sons[MaxSons]; //指向孩子节点
}TSonNode;
  • 孩子兄弟链存储结构
    在这里插入图片描述

孩子兄弟存储结构是为每个节点设计3个域:
一个数据元素域
第一个孩子节点指针域
一个兄弟节点指针域

 typedef struct tnode{
   ElemType data;   //节点的值
   struct tnode* hp;//指向兄弟
   struct tnode* vp;//指向孩子节点
 }TSBNode;

二叉树

满二叉树:

  • 如果所有分支节点都有双分节点
  • 并且叶节点都集中在二叉树的最下一层
  • 高度为 h h 的二叉树恰好有 2 h 1 2^h-1 个节点

完全二叉树

  • 最多只有下面两层的节点的度数小于2
  • 并且最下面一层的叶节点都依次排列在该层最左边的位置上
    在这里插入图片描述

完全二叉树实际上是对应的满二叉树删除叶节点层最右边若干节点得到的。

二叉树性质

  • 性质1: 非空二叉树上叶节点数等于双分支节点数加1.即: n 0 = n 2 + 1 n_0=n_2+1
  • 性质2: 非空二叉树上第i层上至多有 2 i 1 2^i-1 个节点( i 1 i\geq1
  • 性质3: 高度为h的二叉树至多有 2 h 1 2^h-1 个节点( h 1 h\geq1
  • 性质4: 完全二叉树性质(含 n n 个节点)

1. n 1 = 0 n_1=0 或者 n 1 = 1 n_1=1 n 1 n_1 可由 n n 的奇偶性确定:
在这里插入图片描述

2.若 i n / 2 i\leq{n/2} ,则编号为i的节点为分支节点,否则为叶节点
3.除树根节点外,若一个节点的编号为i,则它的双亲节点的编号为 i / 2 i/2
4.若编号为i的节点有左孩子节点,则左孩子节点的编号为 2 i 2i ;若编号为i的节点有右孩子节点,则右孩子节点的编号为 2 i + 1 2i+1

二叉树遍历

  • 前序遍历: 根节点->左子树->右子树(先遍历根节点,然后左右)
  • 中序遍历: 左子树->根节点->右子树(在中间遍历根节点)
  • 后序遍历: 左子树->右子树->根节点(最后遍历根节点)
  • 层次遍历: 按层次遍历

二叉树的顺序存储结构

  • 完全二叉树节点按层序编号:
    在这里插入图片描述

  • 完全二叉树的顺序存储结构
    在这里插入图片描述

  • 一般的二叉树先用空节点补全成完全二叉树,然后对节点编号

typedef ElemType SqBTree[MaxSize];

二叉树顺序存储结构的特点:

  • 对于完全二叉树来说,其顺序存储结构是十分合适的
  • 对于一般的二叉树,特别是对于那些单分支节点较多的二叉树来说是很不合适的,因为可能只有少数存储单元被利用,特别是对退化的二叉树(即每个分支节点都是单分支的),空间浪费更是惊人
  • 在顺序存储结构中,找一个节点的双亲和孩子都很容易

二叉树的链式存储结构

typedef struct node{
    ElemType data;
    struct node* lchild;    //左边子节点
    struct node* rchild;    //右边子节点
}BTNode;

二叉链存储结构的特点:

  • 除了指针外,二叉链比较节省存储空间。占用的存储空间与树形没有关系,只与树中节点个数有关。
  • 在二叉链中,找一个节点的孩子很容易,但找其双亲不方便。

二叉树基本运算及其实现

二叉树的基本运算概述

  • 创建二叉树CreateBTNode(*b,*str):根据二叉树括号表示法字符串str生成对应的二叉链存储结构b
  • 销毁二叉链存储结构DestroyBT(*b):销毁二叉链b并释放空间
  • 查找节点FindNode(*b,x):在二叉树b中寻找data域值为x的节点,并返回指向该节点的指针
  • 找孩子节点LchildNode§和RchildNode§:分别求二叉树中节点*p的左孩子节点和右孩子节点
  • 求高度BTNodeDepeh(*b):求二叉树b的高度。若二叉树为空,则其高度为0;否则,其高度等于左子树与右子树中的最大高度加1
  • 输出二叉树DispBTNode(*b):以括号表示法输出一棵二叉树
void CreatreBTNode(BTNode*&b,char* str){
    BTNode* St[MaxSize];
    BTNode* p;
    int top=-1,k,j=0;
    char ch;
    b = NULL;               //建立二叉链初始时为空
    ch = str[j];
    while(ch != '\0'){      //str未扫描完时循环
        switch(ch){
            case '(':
                top++;
                St[top]=p;
                k=1;
                break;      //可能有左孩子节点,进栈
            default:
                p=(BTNode*)malloc(sizeof(BTNode));
                p->data=ch;
                p->lchild=NULL;
                p->rchild=NULL;
                if(b == NULL)
                    b=p;
                else{
                    switch(k){
                        case 1:
                            St[top]->lchild=p;
                            break;
                        case 2:
                            St[top]->rchild=p;
                            break;
                    }
                }
        }
        j++;
        ch=str[j];          //继续扫描str
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_42285618/article/details/107704810