二叉树 二叉树的性质 存储结构 遍历二叉树 C实现二叉树的创建和遍历 线索二叉树

定义

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

二叉树的特点

  • 每个结点最多有两棵子树,所以二叉树总没有度大于2的结点
  • 左子树和右子树是有顺序的,次数不能任意颠倒
  • 即使树中某结点只有一棵子树,也要区分是左子树还是右子树

特殊的二叉树

1. 斜树

所有的结点都只有左子树的二叉树称为左斜树;
所有的结点都只有右子树的二叉树称为右斜树;
这两者统称为斜树

2. 满二叉树

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

特点

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

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

满二叉树一定是完全二叉树, 但完全二叉树不一定是满二叉树

特点

  • 叶子结点只能出现在最下两层.
  • 最下层的叶子一定集中在左部连续位置.
  • 倒数二层,若有叶子节点,一定在右部连续位置
  • 如果结点度为1,则结点只有左孩子,即不存在只有右子树的情况
  • 同样结点数的二叉树,完全二叉树的深度最小

二叉树的性质

性质1

在二叉树的第i层上至多有 2^(i-1) 个结点(i>=1)

性质2

深度为k的二叉树至多有 2^(k)-1 个结点(k>=1)

性质3

对任何一棵二叉树T,如果其终端结点数的n0, 度为2的结点数为n2 则n0 = n2 + 1
树T结点的总数为n=n0+n1+n2
分支结点数= n - 1 = n1 + 2*n2
性质4

具有n个结点的完全二叉树的深度为└ log2^n ┘ + 1 (└ x ┘表示不大于x的最大整数)

性质5

如果对一棵有n个结点的完全二叉树(其深度为 └ log2^n ┘ + 1 )结点按层序编号(从第1层到 └ log2^n ┘ + 1 层,每层从左到右)对任一结点i(1 <= i <=n)有

  • 如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲结点是: └ i/2┘
  • 如果2i>n,则结点i无左孩子(结点i为叶子结点);否则器左孩子是结点2i
  • 如果2i+1>n,则结点i无右孩子,否则其右孩子是结点2i+1
二叉树的存储结构
1. 顺序存储结构

由于二叉树的严格的定义 可以采用一维数组存储二叉树中的结点,并且结点的存储位置,也就是数组的下标要能提现结点之间的逻辑关系,比如双亲和孩子的关系,左右兄弟的关系等.

对于一般的二叉树,尽管层序编号不能反映逻辑关系, 可把不存在的结点设置为^便可.

顺序存储结构一般只用于完全二叉树

2. 二叉链表

二叉树每个结点做多有两个孩子,所以他设计一个数据域和两个指针域是比较自然的想法,我们称这样的链表为二叉链表.

左孩子指针域数据域右孩子指针域
childdatarchild
遍历二叉树

二叉树的遍历是指(traversing binary tree): 从根结点出发,按照某种次序一次访问二叉树中所有的结点,使得每个结点被访问一次且仅被访问一次.

1. 前序遍历

若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,在前序遍历右子树.

2. 中序遍历

若二叉树为空,则空操作返回,否则先从根结点开始(并不是先访问根结点),中序遍历根结点的左子树,然后访问根结点,最后中序遍历右子树.

3. 后序遍历

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

4. 层序遍历

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

四种遍历方式,都是把树中的结点变成某种意义的线性序列,给程序的实现带来了好处

代码实现二叉树的建立和遍历

/*
    二叉树(binary tree) - 链式存储结构
    实现 二叉树的创建和遍历
    遍历和创建分为三种 前序,中序,后序 
*/
#include <stdio.h>
#include <malloc.h>

typedef struct TreeNode{
    char data;//数据域 
    struct TreeNode * lchild, * rchild; //指针域 
}TreeNode;

//前序方式生成二叉树 
void createTree(TreeNode * &T)
{
    char data;
    scanf("%c",&data);
    if(data == '#') //补位叶子结点 
    {
        T = NULL;
        return;

    }else{

        T = (TreeNode *)malloc(sizeof(TreeNode));
        if(T == NULL)
        {
            exit(-1);
        }
        T->data = data;
        createTree(T->lchild);
        createTree(T->rchild);
    }
}


//前序遍历二叉树 
void preOrderTraversal(TreeNode * T)
{
    if(T == NULL)
    {
        return;
    }
    printf("%c \n",T->data);
    preOrderTraversal(T->lchild);
    preOrderTraversal(T->rchild);
}
//后序遍历二叉树 
void afterOrderTraversal(TreeNode * T)
{
    if(T == NULL)
    {
        return;
    }
    preOrderTraversal(T->lchild);
    preOrderTraversal(T->rchild);
    printf("%c \n",T->data);
}
//中序遍历二叉树 
void mediumOrderTraversal(TreeNode * T)
{
    if(T == NULL)
    {
        return;
    }
    preOrderTraversal(T->lchild);
    printf("%c \n",T->data);
    preOrderTraversal(T->rchild);
}


int main(void)
{   
    TreeNode Tree;
    TreeNode * T = &Tree ;
    createTree(T);
    printf("二叉树创建成功\n");
    printf("前序遍历\n");
    preOrderTraversal(T);
    printf("后序遍历\n");
    afterOrderTraversal(T); 
    printf("中序遍历\n");
    mediumOrderTraversal(T);

    return 0;
}

注意: 建立的时候 每个结点确定是否有左右孩子,如果没有则用#代替

缺点

浪费空间,有很多的空指针域 最有有n+1个空指针

二叉树的建立和遍历都是递归的原理
建立 是在原来的打印结点的地方,改成了生成节点,给结点赋值的操作而已.
n个结点有2n个指针域,而n个结点的二叉树一共有n-1条分支线,所以空指针域 = 2n-(n-1) = n+1

线索二叉树

指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树
为了解决二叉树中空指针域浪费的问题 将空指针利用起来,指向前驱和后继 这种思想称为线索二叉树

优点

便于查找结点
遍历二叉树效率高
便于超找结点的前驱和后继.

原文链接:http://blog.youkuaiyun.com/u010187139/article/details/46778661

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值