数据结构与算法(10)- 二叉树

182 阅读13分钟

结点:是数据结构中的基础,是构成复杂数据结构的基本组成单位

树(Tree): 是 n(n >= 0) 个结点的有限集,n = 0 时称为空树,在任意一颗非空树种中:

  1. 有且仅有一个特定的结点称为根(Root)结点
  2. 当 n > 1 时,其余结点可以分为 m(m > 0)个互不相交的有限集 T1、T2、...、Tn,其中每一个集合本身又是一棵树,并且称为根的子树。
  3. n > 0 时,根结点是唯一的,不可能存在多个根结点,数据结构中的数只能有一个根结点。
  4. m > 0 时,子树的个数没有限制,但它们一定是互不相交的。

一般树

结点关系

  1. 结点子树的根结点为根结点的孩子结点,孩子结点的根结点为孩子结点的双亲结点
  2. 同一个双亲结点的孩子结点之间互称兄弟结点

二叉树

二叉树是 n(n >= 0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两颗互不相交的、分别称为根结点的左子树和右子树组成。

二叉树的五种形态

斜树

满二叉树

在一颗二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树

满二叉树特点:

  1. 叶子只能出现在最下一层,出现在其他层就不可能达成平衡。
  2. 非叶子结点的度一定是2
  3. 在同样深度的二叉树中,满二叉树的结点个数最多,叶子最多

完全二叉树

对一颗具有 n 个结点的二叉树按层编号,如果编号为 i(1 <= i <=n)的结点与同样深度的满二叉树中编号为 i 的结点在二叉树中位置完全相同,则这颗二叉树称为完全二叉树

特点

  1. 叶子结点只能出现在最下层和次下次
  2. 最下层的叶子结点集中在树的左部
  3. 倒数第二层若存在叶子结点,一定在右部连续位置。
  4. 如果结点度为 1,则该结点只有左孩子,没有右孩子
  5. 同样结点数目的二叉树,完全二叉树深度最小

注意:满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树

二叉树的特性

  1. 每个节点最多有两颗子树,所以二叉树中不存在度大于 2 的结点
  2. 左子树和右子树是由顺序的,次序不能任意颠倒
  3. 即使数中某结点只有一颗子树,也不要区分它是左子树还是右子树。
  4. 叶子结点:度为 0 的结点

二叉树性质

  1. 在二叉树的第 i 层最多有 2^(i-1) 个结点。(i >= 1)
  2. 二叉树中如果深度为 k ,那么最多有 2^k 个结点。(k >= 1)
  3. 在完全二叉树中,具有 n 个结点的完全二叉树深度为 [log2n] + 1,其中 [log2n] 是向下取整。
  4. 若对含 n 个结点的完全二叉树从上到下且从左到右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点有如下特性:
  • 如果 i>1,那么序号为 i 的结点的双亲结点序号为 i/2;
  • 如果 i=1,那么序号为 i 的结点为根节点,无双亲结点;
  • 如果 2i <= n, 那么序号为 i 的结点的左孩子序号为 2i;
  • 如果 2i > n, 那么序号为 i 的结点无左孩子;
  • 如果 2i + 1 <= n,那么序号为 i 的结点右孩子需要为 2i+1;
  • 如果 2i + 1 > n,那么序号为 i 的结点无右孩子。

二叉树顺序存储

二叉树的顺序存储结构就是使用一维数组存储二叉树中的结点,并且结点的存储位置,就是数组的下标索引

完全二叉树的顺序存储方式

上图便是一颗完全二叉树的顺序存储方式示意图

非完全二叉树的顺序存储方式

图中 '^' 符号表示此位置为空,没有存储结点。通过图就可以观察出,通过顺序存储会造成一定空间的浪费,因此顺序存储一般适用于完全二叉树

二叉树的顺序存储的代码实现

  • 基本功能
#include "stdio.h"
#include "stdlib.h"

#include "math.h"
#include "time.h"

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

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

typedef int Status;        /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Element;
typedef Element sqBiTree[MAX_TREE_SIZE];
Element Nil = 0;//设整型以0为空

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

//1.visit
Status visit(Element e) {
    printf("%d ",e);
    return OK;
}

//2.构造空二叉树T,因为T是固定数组,不会改变.
Status initSqBiTree(sqBiTree T) {
    for (int i = 0; i < MAX_TREE_SIZE; i++) {
        T[i] = Nil;
    }
    return OK;
}

//按层序次序输入二叉树中的结点值(字符型或整型),构造顺序存储的二叉树T
Status createSqBiTree(sqBiTree T) {
    int i = 0, value;
    
    printf("按层序输入结点的值(整型),0表示空结点, 输入999结束.结点数<=%d\n",MAX_TREE_SIZE);
    while (1) {
        scanf("%d",&value);
        //如果输入的值为 999 ,或者第一个数输入0时,跳出循环,上面以设定,为 0 代表结点为空
        if (value == 999 || (i == 0 && value == 0)) {
            break;
        }
        
        T[i] = value;
        //结点不为空,并且无双亲结点, i/2 便是双亲结点的位置
        if (i != 0 && T[(i+1)/2-1] == Nil && T[i] == Nil) {
            printf("出现双亲结点的b非根节点:%d\n",T[i]);
            return ERROR;
        }
        i ++;
    }
    
    //将 0 赋给 T 后面的结点
    for (; i < MAX_TREE_SIZE; i++) {
        T[i] = Nil;
    }
    return OK;
}

//4 判断⼆叉树是否为空
int isEmpty(sqBiTree T) {
    if (T[0] != Nil) {
        return FALSE;
    }
    return TRUE;
}

//5 获取二叉树的深度
int getDeepOfSqBiTree(sqBiTree T) {
    int i;
    int j = -1;
    //找到二叉树中最后一个结点
    for (i = MAX_TREE_SIZE-1; i >= 0; i--) {
        //从后往前找,第一个不为 Nil 的元素,变为最后一个结点
        if (T[0] != Nil) {
            break;
        }
    }
    do {
        j++;
    } while (powl(2, j) <= i);//计算2的次幂
    return j;
}

//6 返回处于位置e(层,本层序号)的结点值
Element value(sqBiTree T, Position p) {
    //因为根结点的位置从 0 开始的
    int level = (int)pow(2, p.level-1);
    //order 是从0开始的不是1 所以要减1 ,然后 2 次幂算出来已经在第 level 层的第一个,所以要再减 1,加起来就是减 2
    int order = p.order-2;
    return T[level + order];
}

//7 获取二叉树跟结点的值
Element rootValue(sqBiTree T) {
    if (isEmpty(T)) {
        return ERROR;
    }
    return T[0];
}

//8 给处于位置e的结点赋值
//初始条件: 二叉树存在,e是T中某个结点的位置
//操作结果: 给处于位置e的结点赋值Value;
Status Assign(sqBiTree T,Position e,Element value) {
    int level = pow(2, e.level-1);
    int order = e.order - 2;
    
    int i = level + order;
    //叶子结点的双亲为空
    if (value != Nil && T[(i + 1)/2-1] == Nil) {
        return ERROR;
    }
//    给双亲赋空值但是有叶子结点
    if (value == Nil && T[i*2+1] != Nil) {
        return ERROR;
    }
    T[i] = value;
    return TRUE;
}

//9 获取e的双亲
Element Parent(sqBiTree T, Element e) {
    if (isEmpty(T)) {
           return Nil;
    }
    int i ;
    for (i = 0; i < MAX_TREE_SIZE; i++) {
        if (T[i] == e) {
            return T[(i+1)/2-1];
        }
    }
    return Nil;
}

//10 获取某个结点的左孩⼦
Element leftChild(sqBiTree T, Element e) {
    if (isEmpty(T)) {
           return Nil;
    }
    int i ;
    for (i = 0; i < MAX_TREE_SIZE; i++) {
        if (T[i] == e) {
            return T[(i * 2)+1];
        }
    }
    return Nil;
}

//11 获取某个结点的右孩⼦;
Element rightChild(sqBiTree T, Element e) {
    if (isEmpty(T)) {
           return Nil;
    }
    int i ;
    for (i = 0; i < MAX_TREE_SIZE; i++) {
        if (T[i] == e) {
            return T[(i * 2)+2];
        }
    }
    return Nil;
}

//12 获取结点的左兄弟
Element leftBrother(sqBiTree T, Element e) {
    if (isEmpty(T)) {
           return Nil;
    }
    int i;
    for (i = 0; i < MAX_TREE_SIZE; i++) {
        if (T[i] == e && i%2 == 0) {
            return T[i-1];
        }
    }
    return Nil;
}

//13 获取结点的右兄弟
Element rightBrother(sqBiTree T, Element e) {
    if (isEmpty(T)) {
           return Nil;
    }
    int i;
    for (i = 0; i < MAX_TREE_SIZE; i++) {
        if (T[i] == e && i%2 == 1) {
            return T[i+1];
        }
    }
    return Nil;
}

#pragma mark -- 二叉树的遍历

/*
 6.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]);
        }
    }
    printf("\n");
}

/*
6.15 前序遍历二叉树
*/
void preTraverse(sqBiTree T,int e) {
    visit(T[e]);
    
    //遍历左子树,地址为奇数
    if (T[(e * 2) + 1] != Nil) {
        preTraverse(T, (e * 2) + 1);
    }
    //遍历右子树,地址为偶数
    if (T[(e * 2) + 2] != Nil) {
        preTraverse(T, (e * 2) + 2);
    }
}

Status preOrderTraverse(sqBiTree T){
    
    //树不为空
    if (!isEmpty(T)) {
        preTraverse(T, 0);
    }
    printf("\n");
    return  OK;
}

/*
6.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 (!isEmpty(T)) {
        inTraverse(T, 0);
    }
    printf("\n");
    return OK;
}

/*
6.17 后序遍历
*/
void postTraverse(sqBiTree T,int e) {
    if (T[e * 2 + 1] != Nil) {
        postTraverse(T, e);
    }
    if (T[e * 2 + 2] != Nil) {
        postTraverse(T, e);
    }
    visit(T[e]);
}

Status postOrderTraverse(sqBiTree T)
{
    if(!isEmpty(T)) /* 树不空 */
        postTraverse(T,0);
    printf("\n");
    return OK;
}


int main(int argc, const char * argv[]) {
    // insert code here...
    printf("二叉树顺序存储结构实现!\n");
    Status iStatus;
    Position p;
    Element e;
    sqBiTree T;
    
    initSqBiTree(T);
    createSqBiTree(T);
    printf("建立二叉树后,树空否?%d(1:是 0:否) \n",isEmpty(T));
    printf("树的深度=%d\n",getDeepOfSqBiTree(T));
    
    p.level=3;
    p.order=2;
    e=value(T,p);
    printf("第%d层第%d个结点的值: %d\n",p.level,p.order,e);
    
    
    iStatus = rootValue(T);
    if (iStatus) {
        printf("二叉树的根为:%d\n",e);
    }else
    {
        printf("树为空,无根!\n");
    }
    
    //向树中3层第2个结点位置上结点赋值99
    e = 99;
    Assign(T, p, e);
    
    //获取树中3层第2个结点位置结点的值是多少:
    e=value(T,p);
    printf("第%d层第%d个结点的值: %d\n",p.level,p.order,e);
    
    //找到e这个结点的双亲;
    printf("结点%d的双亲为%d_",e,Parent(T, e));
    //找到e这个结点的左右孩子;
    printf("左右孩子分别为:%d,%d\n",leftChild(T, e),rightChild(T, e));
    //找到e这个结点的左右兄弟;
    printf("结点%d的左右兄弟:%d,%d\n",e,leftBrother(T, e),rightBrother(T, e));
    
    
    Assign(T, p, 5);
    
    printf("二叉树T层序遍历:");
    levelOrderTraverse(T);
    
    printf("二叉树T先序遍历:");
    preOrderTraverse(T);
    
    printf("二叉树T中序遍历:");
    inOrderTraverse(T);
    
    printf("二叉树T后序遍历:");
   
    return 0;
}

打印结果:

  • 二叉树的遍历

是指的从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点访问一次且仅被访问一次。

  • 层数遍历

/*
 6.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]);
        }
    }
    printf("\n");
}

打印结果:

  • 前序遍历

从二叉树的根结点出发,当第一次到达结点是就输出结点数据,按照先向左再向右的方向访问

/*
6.15 前序遍历二叉树
*/
void preTraverse(sqBiTree T,int e) {
    visit(T[e]);
    
    //遍历左子树,地址为奇数
    if (T[(e * 2) + 1] != Nil) {
        preTraverse(T, (e * 2) + 1);
    }
    //遍历右子树,地址为偶数
    if (T[(e * 2) + 2] != Nil) {
        preTraverse(T, (e * 2) + 2);
    }
}

Status preOrderTraverse(sqBiTree T){
    
    //树不为空
    if (!isEmpty(T)) {
        preTraverse(T, 0);
    }
    printf("\n");
    return  OK;
}

打印结果:

  • 中序遍历

从根结点开始(注意并不不是先访问根结点), 中序遍历根结点的左⼦子树,然后是访问根结点,最后中序遍历右⼦子树

/*
6.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 (!isEmpty(T)) {
        inTraverse(T, 0);
    }
    printf("\n");
    return OK;
}

打印结果:

  • 后序遍历

从左到右先叶子后结点的方式遍历左右子树,最后访问根结点

/*
6.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(!isEmpty(T)) /* 树不空 */
        postTraverse(T,0);
    printf("\n");
    return OK;
}

打印结果:

二叉树链式存储

二叉数赋值

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

定义结构

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

链式二叉树示意图

基本操作

/**2、打印数据*/
ret visit(Element e) {
    printf("%c", e);
    return OK;
}

/**3、构造空二叉树*/
ret initBiTree(biTree *T) {
    *T = NULL;
    return OK;
}

/**4、 创建二叉树
    按前序输入二叉树中的结点值(字符),#表示空树;
 */
ret creatBiTree(biTree *T) {
   //获取字符
    Element e = str[indexs++];
    if (e == '#') {
        *T = NULL;
    } else  {
        *T = (biTree)malloc(sizeof(biTree));
        //是否创建成功
        if (!*T) {
          return ERROR;
        }
        /* 生成根结点 */
        (*T)->data = e;
        /* 构造左子树 */
        creatBiTree(&(*T)->lChild);
        /* 构造右子树 */
        creatBiTree(&(*T)->rChild);
    }
    return OK;
}

/**5、销毁二叉树*/
ret destroyBiTree(biTree *T) {
    if ( *T == NULL) {
        return ERROR;
    }
    
    /* 销毁左孩子子树 */
    if ((*T)->lChild) {
        destroyBiTree(&(*T)->lChild);
    }
    
    /* 销毁右孩子子树 */
    if ((*T)->rChild) {
        destroyBiTree(&(*T)->rChild);
    }
    
    /**释放结点*/
    free(*T);
    
    *T = NULL;
    return OK;
}

/**6、二叉树T是否为空*/
ret biTreeEmpty(biTree T) {
    if (T) {
        return ERROR;
    } else {
        return OK;
    }
}

/**7 二叉树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;
    }
    return i > j ? i + 1:j + 1;
}

/**8 二叉树T的根*/
Element root(biTree T) {
    if (biTreeEmpty(T)) {
        return Nil;
    }
    return T->data;
}

/**9 返回p所指向的结点值*/
Element value(biTree p){
    return p->data;
}

/**10 给p所指结点赋值为value;
初始条件: 二叉树T存在,p指向T中某个结点
操作结果: 给p所指结点赋值为value
 */
void Assign(biTree p,Element value)
{
    p->data=value;
}

int main(int argc, const char * argv[]) {
    // insert code here...
    printf("二叉树链式存储实现!\n");
    int i;
    biTree T;
    Element e1;
    
    initBiTree(&T);
    
    strAssign(str,"ABDH#K###E##CFI###G#J##");
    
    creatBiTree(&T);
    printf("二叉树是否为空%d(1:是 0:否),树的深度=%d\n",biTreeEmpty(T),BiTreeDepth(T));
    
    e1=root(T);
    printf("二叉树的根为: %c\n",e1);
    
    printf("\n前序遍历二叉树:");
    preOrderTraverse(T);
    
    printf("\n中序遍历二叉树:");
    inOrderTraverse(T);
    
    printf("\n后序遍历二叉树:");
    postOrderTraverse(T);
    printf("\n");
    return 0;
}

打印结果:

前序遍历

/*
11  前序递归遍历T
初始条件:二叉树T存在;
操作结果: 前序递归遍历T
*/
void preOrderTraverse(biTree T) {
    if (T == NULL) {
        return;
    }
    visit(T->data);/* 显示结点数据*/
    preOrderTraverse(T->lChild);/* 再先序遍历左子树 */
    preOrderTraverse(T->rChild); /* 最后先序遍历右子树 */
}

打印结果:

中序遍历

/*
12  中序递归遍历T
初始条件:二叉树T存在;
操作结果: 中序递归遍历T
*/
void inOrderTraverse(biTree T)
{
    if(T==NULL)
        return ;
    inOrderTraverse(T->lChild); /* 中序遍历左子树 */
    visit(T->data);/* 显示结点数据*/
    inOrderTraverse(T->rChild); /* 最后中序遍历右子树 */
}

打印结果:

###后续遍历

/*
 7.10  后序递归遍历T
 初始条件:二叉树T存在;
 操作结果: 中序递归遍历T
 */
void postOrderTraverse(biTree T)
{
    if(T==NULL)
        return;
    postOrderTraverse(T->lChild); /* 先后序遍历左子树  */
    postOrderTraverse(T->rChild); /* 再后序遍历右子树  */
    visit(T->data);/* 显示结点数据*/
}

打印结果: