数据结构与算法——二叉树

435 阅读11分钟

1.树

树结构是一种非线性存储结构,存储的是具有“一对多”关系的数据元素的集合。

上图是使用树结构存储的集合 {A,B,C,D,E,F,G,H,I,J,K,L,M} 的示意图。对于数据 A 来说,和数据 B、C、D 有关系;对于数据 B 来说,和 E、F 有关系。这就是“一对多”的关系。

将具有“一对多”关系的集合中的数据元素按照左图的形式进行存储,整个存储形状在逻辑结构上看,类似于实际生活中倒着的树(右图),所以称这种存储结构为“树型”存储结构。

结点:使用树结构存储的每一个数据元素都被称为“结点”。例如,上图中元素 A 就是一个结点;

父结点(双亲结点)、子结点和兄弟结点:对于上图中的结点 A、B、C、D 来说,A 是 B、C、D 结点的父结点(也称为“双亲结点”),而 B、C、D 都是 A 结点的子结点(也称“孩子结点”)。对于 B、C、D 来说,它们都有相同的父结点,所以它们互为兄弟结点。

树根结点(简称“根结点”):每一个非空树都有且只有一个被称为根的结点。上图中,结点 A 就是整棵树的根结点。 树根的判断依据为:如果一个结点没有父结点,那么这个结点就是整棵树的根结点。 叶子结点:如果结点没有任何子结点,那么此结点称为叶子结点(叶结点)。例如上图中,结点 K、L、F、G、M、I、J 都是这棵树的叶子结点。

子树:如上图,整棵树的根结点为结点 A,而如果单看结点 B、E、F、K、L 组成的部分来说,也是棵树,而且节点 B 为这棵树的根结点。所以称 B、E、F、K、L 这几个结点组成的树为整棵树的子树;同样,结点 E、K、L 构成的也是一棵子树,根结点为 E。 注意:单个结点也是一棵树,只不过根结点就是它本身。上图中,结点 K、L、F 等都是树,且都是整棵树的子树。

知道了子树的概念后,树也可以这样定义:树是由根结点和若干棵子树构成的。

空树:如果集合本身为空,那么构成的树就被称为空树。空树中没有结点。 补充:在树结构中,对于具有同一个根结点的各个子树,相互之间不能有交集。例如,图 1(A)中,除了根结点 A,其余元素又各自构成了三个子树,根结点分别为 B、C、D,这三个子树相互之间没有相同的结点。如果有,就破坏了树的结构,不能算做是一棵树。

对于一个结点,拥有的子树数(结点有多少分支)称为结点的度(Degree)。例如,上图中,根结点 A 下分出了 3 个子树,所以,结点 A 的度为 3。 一棵树的度是树内各结点的度的最大值。上图表示的树中,各个结点的度的最大值为 3,所以,整棵树的度的值是 3。

结点的层次:从一棵树的树根开始,树根所在层为第一层,根的孩子结点所在的层为第二层,依次类推。对于上图来说,A 结点在第一层,B、C、D 为第二层,E、F、G、H、I、J 在第三层,K、L、M 在第四层。

2.二叉树

简单地理解,满足以下两个条件的树就是二叉树:
1.本身是有序树;
2.树中包含的各个节点的度不能超过 2,即只能是 0、1 或者 2;

二叉树具有以下几个性质:
1.二叉树中,第 i 层最多有 2i-1 个结点。
2.如果二叉树的深度为 K,那么此二叉树最多有 2K-1 个结点。
3.二叉树中,终端结点数(叶子结点数)为 n0,度为 2 的结点数为 n2,则 n0=n2+1。

2.1满二叉树

如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树。

1.满二叉树除了满足普通二叉树的性质,还具有以下性质:
2.满二叉树中第 i 层的节点数为 2n-1 个。
3.深度为 k 的满二叉树必有 2k-1 个节点 ,叶子数为 2k-1。
4.满二叉树中不存在度为1的节点,每一个分支点中都两棵深度相同的子树,且叶子节点都在最底层。
5.具有 n 个节点的满二叉树的深度为 log2(n+1)。

2.2完全二叉树

如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。

完全二叉树除了具有普通二叉树的性质,它自身也具有一些独特的性质,比如说,n 个结点的完全二叉树的深度为 ⌊log2n⌋+1。
⌊log2n⌋ 表示取小于 log2n 的最大整数。例如,⌊log24⌋ = 2,而 ⌊log25⌋ 结果也是 2。

对具有n个结点的完全二叉树,如果按照从上至下和从左至右的顺序对二 叉树的所有结点从1开始编号,则对于任意的序号为i的结点有:
A.如果i>1,那么序号为i的结点的双亲结点序号为i/2;
B.如果i=1,那么序号为i的结点为根节点,无双亲结点;
C.如果2i<=n,那么序号为i的结点的左孩子结点序号为2i;
D.如果2i>n,那么序号为i的结点无左孩子;
E.如果2i+1<=n,那么序号为i的结点右孩子序号为2i+1;
F.如果2i+1>n,那么序号为i的结点无右孩子。

3.二叉树的顺序存储

基础类型定义

#define MAXSIZE 100 /* 存储空间初始分配量 */  
#define MAX_TREE_SIZE 100 /* 二叉树的最大结点数 */  

typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int CElemType;/* 树结点的数据类型,目前暂定为整型 */

typedef CElemType SqBiTree[MAX_TREE_SIZE]; /* 0号单元存储根结点  */
CElemType Nil = 0;  /*设整型以0为空 或者以 INT_MAX(65535)*/

typedef struct {
    int level; //结点层
    int order; //本层的序号(按照满二叉树给定序号规则)
} Position;

3.1 visit数据访问

Status visit(CElemType c){
    printf("%d ",c);
    return OK;
}

3.2 构造空二叉树T,因为T是固定数组,不会改变.

Status InitBiTree(SqBiTree T){
    for (int i=0; i<MAX_TREE_SIZE; i++) {
        T[i] = Nil;
    }
    return OK;
}

3.3 按层序次序输⼊入二叉树中的结点值(字符型或整型),构造顺序存储的二叉树T

Status CreateBiTree(SqBiTree T){
    int i = 0;
    while (i<10) {
        T[i] = i+1;
        if (i!=0 && T[(i+1)/2-1] == Nil && T[i] !=Nil) {
            printf("出现无双亲的非根结点%d\n",T[i]);
            exit(ERROR);
        }
        i++;
    }
    
     //将空赋值给T的后面的结点
    while (i<MAX_TREE_SIZE) {
        T[i] = Nil;
        i++;
    }
    return OK;
}

3.4 判断⼆叉树是否为空

Status BiTreeEmpty(SqBiTree T){
    if (T[0]==Nil) {
        return TRUE;
    }
    return FALSE;
}

3.5 获取⼆叉树的深度

int BiTreeDepth(SqBiTree T){
    int j = -1;
    int i;
    for (i=MAX_TREE_SIZE; i>=0; i--) {
        if (T[i]!=Nil) {
            break;
        }
    }
    
    do {
        j++;
    } while (pow(2, j)<=i);
    
    return j;
}

3.6 返回处于位置e(层,本层序号)的结点值

CElemType Value(SqBiTree T,Position e){
    /*
    Position.level -> 结点层.表示第几层;
    Position.order -> 本层的序号(按照满二叉树给定序号规则)
    */
    
    //第level层 在数组中的起始下标
    int i = (int)pow(2, e.level-1) - 1;
    printf("%d\n",(int)pow(2,e.level-1));
    
    int j = e.order - 1;
    //e.order
    printf("%d\n",e.order);
    
    return T[i+j];
}

3.7 获取⼆叉树跟结点的值

Status Root(SqBiTree T,CElemType *e){
    if (BiTreeEmpty(T)) {
        return ERROR;
    }
    *e = T[0];
    return OK;
}

3.8 给处于位置e的结点赋值

Status Assign(SqBiTree T,Position e,CElemType value){
    //找到当前e在数组中的具体位置索引
    int i = (int)powl(2, e.level-1)+e.order -2;
    
    //叶子结点的双亲为空
    if (value != Nil &&  T[(i+1)/2-1] == Nil) {
        return ERROR;
    }
    
    //给双亲赋空值但是有叶子结点
    if (value == Nil && (T[i*2+1] != Nil || T[i*2+2] != Nil)) {
        return  ERROR;
    }
    
    T[i] = value;
    return OK;
}

3.9 获取e的双亲;

CElemType Parent(SqBiTree T, CElemType e){
    //空树
    if (T[0]==Nil) {
        return Nil;
    }
    for (int i=0; i<MAX_TREE_SIZE; i++) {
         //找到e
        if (T[i] == e) {
            return T[(i+1)/2-1];
        }
    }
    //没有找到
    return Nil;
}

3.10 获取某个结点的左孩⼦;

CElemType LeftChild(SqBiTree T,CElemType e){
    //空树
    if (T[0]==Nil) {
        return Nil;
    }
    for (int i=0; i<MAX_TREE_SIZE; i++) {
         //找到e
        if (T[i] == e) {
            return T[i*2+1];
        }
    }
    //没有找到
    return Nil;
}

3.11 获取某个结点的右孩子;

CElemType RightChild(SqBiTree T,CElemType e){
    //空树
    if (T[0]==Nil) {
        return Nil;
    }
    for (int i=0; i<MAX_TREE_SIZE; i++) {
         //找到e
        if (T[i] == e) {
            return T[i*2+2];
        }
    }
    //没有找到
    return Nil;
}

3.12 获取结点的左兄弟

CElemType LeftSibling(SqBiTree T,CElemType e){
    //空树
    if (T[0]==Nil) {
        return Nil;
    }
    for (int i=0; i<MAX_TREE_SIZE; i++) {
        //找到e且其序号为偶数(是右孩子)
        if (T[i] == e && i%2 == 0) {
            return T[i-1];
        }
    }
    
    //没有找到
    return Nil;
}

3.13 获取结点的右兄弟

CElemType RightSibling(SqBiTree T,CElemType e){
    //空树
    if (T[0]==Nil) {
        return Nil;
    }
    for (int i=0; i<MAX_TREE_SIZE; i++) {
        //找到e且其序号为奇数(是左孩子)
        if (T[i] == e && i%2 == 1) {
            return T[i+1];
        }
    }
    
    //没有找到
    return Nil;
}

3.14 层序遍历二叉树

void LevelOrderTraverse(SqBiTree T){
    int i = MAX_TREE_SIZE - 1;
    while (T[i]==Nil) {
        i--;
    }
    for (int j = 0; j<=i; j++) {
        //只遍历非空结点
        if (T[j] != Nil)
            visit(T[j]);
    }
    
}

3.15 前序遍历二叉树

void PreTraverse(SqBiTree T,int e){
    //打印结点数据
    visit(T[e]);
    
    //先序遍历左子树
    if (T[2*e + 1] != Nil) {
        PreTraverse(T, 2*e+1);
    }
    
    //最后先序遍历右子树
    if (T[2 * e + 2] != Nil) {
        PreTraverse(T, 2*e+2);
    }
}
Status PreOrderTraverse(SqBiTree T){
    //树不为空
    if (!BiTreeEmpty(T)) {
        PreTraverse(T, 0);
    }
    printf("\n");
    return  OK;
}

3.16 中序遍历

void InTraverse(SqBiTree T, int e){
    
    /* 左子树不空 */
    if (T[2*e+1] != Nil)
        InTraverse(T, 2*e+1);
    
    visit(T[e]);
    
    /* 右子树不空 */
    if (T[2*e+2] != Nil)
        InTraverse(T, 2*e+2);
}
Status InOrderTraverse(SqBiTree T){
    
    /* 树不空 */
    if (!BiTreeEmpty(T)) {
        InTraverse(T, 0);
    }
    printf("\n");
    return OK;
}

3.17 后序遍历

void PostTraverse(SqBiTree T,int e)
{   /* 左子树不空 */
    if(T[2*e+1]!=Nil)
        PostTraverse(T,2*e+1);
    /* 右子树不空 */
    if(T[2*e+2]!=Nil)
        PostTraverse(T,2*e+2);
    
    visit(T[e]);
}
Status PostOrderTraverse(SqBiTree T)
{
    if(!BiTreeEmpty(T)) /* 树不空 */
        PostTraverse(T,0);
    printf("\n");
    return OK;
}

4. 二叉树的链式存储

基础类型定义

typedef char CElemType;
CElemType Nil=' '; /* 字符型以空格符为空 */
typedef struct BiTNode  /* 结点结构 */
{
    CElemType data;        /* 结点数据 */
    struct BiTNode *lchild,*rchild; /* 左右孩子指针 */
}BiTNode,*BiTree;

4.1 打印数据

Status visit(CElemType e) {
    printf("%c ",e);
    return OK;
}

4.2 构造空二叉树T

Status InitBiTree(BiTree *T) {
    *T=NULL;
    return OK;
}

4.3 销毁二叉树

void DestroyBiTree(BiTree *T) {
    if (*T) {
        //如果有左孩子
        if ((*T)->lchild) {
            //销毁左孩子
            DestroyBiTree(&(*T)->lchild);
        }
        
        if ((*T)->rchild) {
            DestroyBiTree(&(*T)->rchild);
        }
        
        //释放根结点
        free(*T);
        
        //赋值为空
        *T=NULL;
    }
}

4.4 创建二叉树

//按前序输入二叉树中的结点值(字符),#表示空树;
void CreateBiTree(BiTree *T) {
    CElemType ch;
    //获取字符
    ch = str[indexs++];
    
    //判断当前字符是否为'#'
    if (ch == '#') {
        *T = NULL;
    } else {
        //创建新的结点
        *T = (BiTree)malloc(sizeof(BiTNode));
        //是否创建成功
        if (!*T) {
            exit(OVERFLOW);
        }
        
        /* 生成根结点 */
        (*T)->data = ch;
        /* 构造左子树 */
        CreateBiTree(&(*T)->lchild);
        /* 构造右子树 */
        CreateBiTree(&(*T)->rchild);
    }
}

4.5 二叉树T是否为空

Status BiTreeEmpty(BiTree T)
{
    if(T) {
        return FALSE;
    }
    else {
        return TRUE;
    }
}

4.6 二叉树T的深度

int BiTreeDepth(BiTree T){
    if (!T) {
        return 0;
    }
    
    int i,j;
    
    //计算左孩子的深度
    if (T->lchild) {
        i = BiTreeDepth(T->lchild);
    } else {
        i = 0;
    }
    
    //计算右孩子的深度
    if (T->rchild) {
        j = BiTreeDepth(T->rchild);
    } else {
        j = 0;
    }
    
    //比较i和j
    return i>j ? i+1 : j+1;
}

4.7 二叉树T的根

CElemType Root(BiTree T){
    if (BiTreeEmpty(T))
        return Nil;
    
    return T->data;
}

4.8 返回p所指向的结点值

CElemType Value(BiTree p){
    return p->data;
}

4.9 给p所指结点赋值为value

void Assign(BiTree p,CElemType value)
{
    p->data=value;
}

4.10 前序递归遍历T

void PreOrderTraverse(BiTree T)
{
    if(T==NULL)
        return;
    printf("%c",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
    PreOrderTraverse(T->lchild); /* 再先序遍历左子树 */
    PreOrderTraverse(T->rchild); /* 最后先序遍历右子树 */
}

4.11 中序递归遍历T

void InOrderTraverse(BiTree T)
{
    if(T==NULL)
        return ;
    InOrderTraverse(T->lchild); /* 中序遍历左子树 */
    printf("%c",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
    InOrderTraverse(T->rchild); /* 最后中序遍历右子树 */
}

4.12 后序递归遍历T

void PostOrderTraverse(BiTree T)
{
    if(T==NULL)
        return;
    PostOrderTraverse(T->lchild); /* 先后序遍历左子树  */
    PostOrderTraverse(T->rchild); /* 再后序遍历右子树  */
    printf("%c",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
}