数据结构与算法--二叉树的顺序存储&链式存储

508 阅读8分钟

1. 树的相关概念

根节点(Root):树的顶部节点

子节点(Child):离开根节点时直接连接到另一个节点的节点。

叶子节点(Leaf):没有子节点的节点

边(Edge):一个节点与另一个节点之间的连接。

路径(Path):连接节点与子代节点的节点和边的序列。

节点高度(Height):节点和叶子之间最长路径上的边的数量

注意:叶子节点的高度为0,如果树只有一个节点,那么这个节点的高也是0

深度(Depth):从该节点到树的根节点的边数总和。

注意:
需要注意的是根节点的深度(Depth)是0.
从高度和深度的对比,它们的方向刚好是相反的。

度:节点的子树数目

层级(Level):该节点到树的根节点的最长路径的边数总和+1。

双亲节: 树的双亲节点是一个节点,如上图中,4的双亲节点是2

二叉树: 二叉树是一个每个最结最多只能有两个分支的树,左边的分支称之为左子树,右边的分支称之为右子树。二叉树节点的度最大也就是2,而普通的树,节点的度是没有限制的。

完美/满二叉树:

  1. 所有的几点都包含两个子节点
  2. 所有的叶子节点的Height或者Level都相等

完全二叉树:最后一层都是满的(都有两个子节点),并且最后一层的节点是从左往右排列的。

通俗的讲:节点按层从左往右排列。最后一层排满了就是完美二叉树,没有满则是完全二叉树。
完美二叉树一定是完全二叉树,完全二叉树不一定是完美二叉树。

完满二叉树:每个节点都有两个子节点

2. 二叉树的顺序存储

2.1 顺序二叉树的基本操作

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

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

typedef int Status; // Status是函数的类型
typedef int CElemType;      /* 树结点的数据类型,目前暂定为整型 */
typedef CElemType SqBiTree[MAX_TREE_SIZE]; /* 0号单元存储根结点  */
CElemType Nill = 0; /*设整型以0为空 或者以 INT_MAX(65535)*/


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

// ✅初始化空树
Status InitBiTree(SqBiTree T) {
    for (int i = 0; i < MAX_TREE_SIZE; i++) {
        //将二叉树初始化值置空
        T[i] = Nill;
    }
    return OK;
}

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

// ✅那层序次序输入二叉树中的结点值,构造二叉树
Status CreatBiTree(SqBiTree T) {
    int i = 0;
    
    while (i < 10) {
        T[i] = i + 1;
        
        // 出现结点不为空,双亲节点为空
        if (i != 0 && T[(i+1)/2-1] == Nill && T[i] == Nill) {
            printf("出现无双亲的非根结点%d\n",T[i]);
        }
        i++;
    }
    // 将空值赋值给后面的节点
    while (i < MAX_TREE_SIZE) {
        T[i] = Nill;
        i++;
    }
    return OK;
}

// ✅清空
// 如果要2个函数的结果一样,但是目的不同;
// 在顺序存储结构中, 两个函数完全一样的结果
#define ClearBiTree InitBiTree

// ✅判断二叉树是否为空,判断根节点
Status BiTreeEmpty(SqBiTree T){
    if (T[0] == Nill) {
        return TRUE;
    }
    return FALSE;
}

// ✅获取二叉树深度
int BiTreeDepth(SqBiTree T)
{
    int j = -1;
    int i;
    // 找到最后一个结点
    for (i = MAX_TREE_SIZE; i >= 0; i--) {
        if (T[i] != Nill) {
            break;
        }
    }
    
    do {
        j++;
    } while (powl(2, j) <= i);//计算2的次幂
    
    return j;
}

// ✅返回处于位置e(层,本层序号)的结点值
CElemType Value(SqBiTree T, Position e){
    //pow(2,e.level-1) 找到层序
    printf("%d\n",(int)pow(2,e.level-1));
    
    //e.order
    printf("%d\n",e.order);
    
    //4+2-2;
    return T[(int)pow(2, e.level-1)+e.order-2];
}

// ✅获取二叉树跟结点的值
Status Root(SqBiTree T,CElemType *e){
    if (BiTreeEmpty(T)) {
        return ERROR;
    }
    
    *e = T[0];
    return OK;
}
// ✅给处于位置e的结点赋值
Status Assign(SqBiTree T,Position e,CElemType value) {
    //找到当前e在数组中的具体位置索引
    int i = (int)pow(2, e.level-1)+e.order-2;
    
    // 判断双亲节点为空
    if (value != Nill &&  T[(i+1)/2-1] == Nill) {
        return ERROR;
    }
    
    //判断自己为空,但是有叶子结点
    if (value == Nill && (T[i*2+1] != Nill || T[i*2+2] != Nill)) {
        return  ERROR;
    }
    T[i] = value;
    return OK;
}

// ✅获取e的双亲
CElemType Parent(SqBiTree T, CElemType e){
    // 空树
    if (T[0] == Nill) {
        return Nill;
    }
    
    for (int i = 1 ; i < MAX_TREE_SIZE; i++) {
        //找到e
        if (T[i] == e) {
            return T[(i+1)/2 - 1];
        }
    }
    
    //没有找到
    return Nill;
}
// ✅获取某个结点的左孩子
CElemType LeftChild(SqBiTree T,CElemType e){
    // 空树
    if (T[0] == Nill) {
        return Nill;
    }
    for (int i = 0; i < MAX_TREE_SIZE; i++) {
        //找到e
        if (T[i] == e) {
            return T[i*2 + 1];
        }
    }
    return Nill;
}
// ✅获取某个结点的右孩子
CElemType RightChild(SqBiTree T,CElemType e){
    
    //空树
    if (T[0] == Nill) {
        return Nill;
    }
    for (int i = 0 ; i < MAX_TREE_SIZE-1; i++) {
        //找到e
        if (T[i] == e) {
            return T[i*2+2];
        }
    }
    
    //没有找到
    return Nill;
    
}

2.2 二叉树的遍历

二叉树的遍历分为以下几种情况:

  • 前序遍历

    规则:若二叉树为空,则空操作返回,否则,先访问根节点,然后前序遍历左子树,再前序遍历右子树

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;
}

  • 中序遍历

    规则:若二叉树为空,则空操作返回,否则,先根节点开始(注意不是先访问根节点),然后中序遍历左子树,然后访问根节,最后再中序遍历右子树

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;
}
  • 后续遍历

    规则:若二叉树为空,则空操作返回,否则,从左到右先叶子结点后结点的方式遍历左右子树,最后访问根节点

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;
}
  • 层序遍历

    规则:若二叉树为空,则空操作返回,否则,从树的第一层,也是就根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个遍历

void LevelOrderTraverse(SqBiTree T){
    
    int i = MAX_TREE_SIZE-1;
    
    //找到最后一个非空结点的序号
    while (T[i] == Nill) i--;
    
    //从根结点起,按层序遍历二叉树
    for (int j = 0; j <= i; j++)
        //只遍历非空结点
        if (T[j] != Nill)
            visit(T[j]);
    
    printf("\n");
}

3. 二叉树的链式存储

3.1 链式二叉树的基本操作

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

/* 存储空间初始分配量 */
#define MAXSIZE 100
/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Status;

#pragma mark--二叉树构造
int indexs = 1;
typedef char String[24]; /*  0号单元存放串的长度 */
String str;
Status StrAssign(String T,char *chars)
{
    int i;
    if(strlen(chars)>MAXSIZE)
        return ERROR;
    else
    {
        T[0]=strlen(chars);
        for(i=1;i<=T[0];i++)
            T[i]=*(chars+i-1);
        return OK;
    }
}

#pragma mark--二叉树基本操作

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

// ✅打印数据
Status visit(CElemType e)
{
    printf("%c ",e);
    return OK;
}

// ✅构造空二叉树T
Status InitBiTree(BiTree *T)
{
    *T=NULL;
    return OK;
}

// ✅销毁二叉树
void DestroyBiTree(BiTree *T)
{
    if(*T)
    {
        /* 有左孩子 */
        if((*T)->lchild)
            DestroyBiTree(&(*T)->lchild); /* 销毁左孩子子树 */
        
        /* 有右孩子 */
        if((*T)->rchild)
            DestroyBiTree(&(*T)->rchild); /* 销毁右孩子子树 */
        
        free(*T); /* 释放根结点 */
        
        *T=NULL; /* 空指针赋0 */
    }
}
#define ClearBiTree DestroyBiTree

// ✅创建二叉树
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);
    }
    
}


// ✅二叉树T是否为空
Status BiTreeEmpty(BiTree T)
{
    if(T)
        return FALSE;
    else
        return TRUE;
}

// ✅二叉树T的深度
int BiTreeDepth(BiTree T){
    
    int i,j;
    if(!T)
        return 0;
    
    //计算左孩子的深度
    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;
    
}

// ✅二叉树T的根
CElemType Root(BiTree T){
    if (BiTreeEmpty(T))
        return Nill;
    
    return T->data;
}

// ✅返回p所指向的结点值
CElemType Value(BiTree p){
    return p->data;
}

// ✅给p所指结点赋值为value
void Assign(BiTree p,CElemType value)
{
    p->data=value;
}

3.2 链式二叉树的遍历

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


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

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