二叉树的学习

二叉树是一种非常常见的数据结构,它结合了有序数组与链表的优点:在二叉树中查找数据与在数组中查找一样快,在二叉树添加、删除数据的速度也和在链表中一样,所以二叉树的相关技术一直是程序员面试笔试中必考的知识点。

- 问题的思考


这里引用一个例子
二叉树,本质上,是对链表和数组的一个折中。。比如,我有一个任务,需要输入 10万个数据(32位整数),然后有两个操作:
1.添加(删除)一个整数。
2.询问第x大的数据。
比如,我给你 1, 8, 13, 10(等等一堆数据)…….然后我询问第3大的数据,然后我插入 18然后我询问第4大的数据我再插入 9我再询问第2大的数据不停的重复1,2重复10万次。。应该如何实现。
你会发现,用有序链表,不行,查找(包括1需要找到对应位置,以及2查找)成本大O(N),但具体这个插入操作成本小O(1)。用有序数组,查找(2的查找)成本小O(1)。但1的插入操作成本很大O(N)。
所以,我们折中使用排序二叉树(二叉树仅仅作为排序二叉树的基础),查找(包括1需要找到对应位置,以及2查找)成本挺小O(logN)。具体这个插入操作成本也挺小O(logN)。

- 基本概念

二叉树的样子

这里写图片描述

  1. 节点的度:节点所拥有的子树的个数成为该节点的度,B的度为2,D的度为0.
  2. 叶节点:度为0的节点成为叶节点,或称终端节点。如D、E、F、G都是。
  3. 分支节点:度不为0的节点成为分支节点,或者成为非终端节点。
  4. 左孩子、右孩子、双亲。树种一个节点的子树的根节点称为这个节点的孩子,这个节点成为它孩子节点的双亲。
  5. 路径、路径长度:如果一棵树的一串节点由n1,n2,……nk有如下关系:节点ni是ni+1的父节点 (1ik) ,就把n1,n2,……nk称为一条由n1->nk的路径,这条路径的长度为k-1.如A->B->D,路径长度为2.
  6. 节点的层数:规定根节点的层数为1,其与节点的层数为它的双亲节点的层数加1。
  7. 树的深度:树中所有节点的最大层数成为树的深度。
  8. 满二叉树:树中所有的分支节点都存在左子树和右子树,且所有的叶节点都在同一层上。上图就是。
  9. 完全二叉树:一颗深度为K的又n个节点的二叉树,树中的节点从上至下,从左到右的顺序进行编号,如果编号为i的节点与满二叉树编号为i的节点在二叉树中的位置相同,则该树为完全二叉树。完全二叉树的特点是叶子节点只能出现在最下层或者次下层。上图去掉G,仍然是完全二叉树,但去掉E就不是了。

#- 基本性质


  1. 一颗非空二叉树第i层上最多有 2i1 个节点.
  2. 一颗深度为 k 的二叉树中,最多具有2k1k
  3. 对于一颗非空完全二叉树,度为0的节点重视比度为2的节点多一个,即若叶节点数为 n0 ,度为2的节点数为 n2 ,则有 n0=n2+1 .
    证明: n0,n1,n2 分别表示叶子节点有0、1、2的节点个数, n 为节点总数,则n=n1+n2+n0,由树和二叉树的性质, n=2×n2+n1 所有节点的度之和+1=节点总数
    联立2式子可得 n2=n01 .
  4. 具有n个节点的完全二叉树的深度为 log2n+1
    证明:由前面的性质,深度为k的二叉树最多只有 zk1 个节点,切完全二叉树的定义是与同深度二叉树前面的编号相同,即它的总节点数N位于 k 层和k1层满二叉树容量之间,即 2k11<n2k1 或者 2k1<n2k ,取对数有 k1log2n<k ,因为k是整数,所以需要取整。
  5. 对于具有n个节点的完全二叉树,如果按照从上至下,从左至右的顺序对二叉树中的所有节点从1开始编号,则对于任意序号为i的节点有:
    1. 如果 i>1 ,则序号为 i 的节点的双亲节点序号为i/2,(“/”表示整除)
    2. 如果 2in ,则序号为i的节点的左孩子结点序号为 2i ;如果 2i>n ,则序号i的节点无左孩子。
  6. 一棵树有N个节点,则边的数目为N-1。

二叉树的构造

二叉树的数据结构类似于链表,不同的是每个节点保存了指向左右孩子的指针

typedef struct BitNode
{
    char data;
    struct BitNode* lchild,*rchild;
    BitNode(char c):data(c),lchild(nullptr),rchild(nullptr){}
}BitNode,*BitTree;

有了数据结构接下来就要构造二叉树了,假设二叉树是这样的:

            a
        b        c
           d   e    f    

那么我们需要加上终止符(比如“#”)来告诉计算机哪些是叶节点。所以加入终止符后的二叉树为:

            a
        b         c
    #     d     e      f
        #   #  #   #  #   # 

我们在构造的时候,一般按照先序遍历的顺序构造,所以上面的二叉树对于的输入为:ab#d##ce##f## .

//二叉树的建立
BitTree CreateBiTree()
{

    char ch;
    BitTree T;
    cin>>ch;
    if(ch=='#')
        T=nullptr;
    else
    {
       T=new BitNode(ch) ;
       T->lchild=CreateBiTree();
       T->rchild=CreateBiTree();

    }
    return T;
}

二叉树的遍历

二叉树的遍历有3种方式

  1. 先序遍历:访问根节点,先序遍历根节点的的左子树,先序遍历根节点的右子树。对应上面的例子为abdcef.
  2. 中序遍历:中序遍历根结点的左子树,访问根节点,中序遍历根节点的右子树。对应上面的例子为bdaecf.
  3. 后续遍历:后序遍历根节点的左子树,后续遍历根节点的右子树,访问根节点。对应上面的例子为dbefca.

对应的代码为:

//先序遍历
void PreOdrderTraverse(BitTree T)
{

    if(T)
    {
        cout<<T->data;
        PreOdrderTraverse(T->lchild);
        PreOdrderTraverse(T->rchild);
    }
}

//中序遍历
void InOrderTraverse(BitTree T)
{

    if(T)
    {
        InOrderTraverse(T->lchild);
        cout<<T->data;
        InOrderTraverse(T->rchild);

    }
}
//后序遍历
void PostOrderTraverse(BitTree T)
{

    if(T)
    {
        PostOrderTraverse(T->lchild);
        PostOrderTraverse(T->rchild);
        cout<<T->data;
    }
}

未完待续……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值