<<大话数据结构>>之浅谈二叉树

编程中我们会遇到多少挫折?表放弃,沙漠尽头必是绿洲。
大话数据结构修改版


学习二叉树的意义


由于二叉树的知识更倾向于理论,所以我们在实际应用开发过程中使用的并不多,但是二叉树作为数据结构的一个重要的组成部分,所以,在程序猿的面试过程中,会经常遇到二叉树知识相关问题.所以学习二叉树是相当有必要的.


二叉树的结构和特点


想要了解二叉树的相关知识,就要先要了解二叉树的结构和特点.只有了解二叉树的结构和它的特点,才能更好的学习二叉树.

我们先看一下二叉树的定义是如何的.

二叉树的结构
二叉树是 n (n >= 0)个结构的有限集合,改集合或者为空集(称为空二叉树),或者有一个根节点和两棵互不相交的、分别称为根节点的左子树和右子树的二叉树组成.
二叉树的特点:
  • 1.每个节点嘴都有两棵子树,所以二叉树中不存在度大于2的结点.注意不是只有两棵子树,而是最多有两棵子树,所以说没有子树或者有一颗子树也是可以的.
  • 2.左子树和右子树是有顺序的,次序不能任意点到.就像人是双手、双脚,但显然左手、左脚和右手、右脚是不一样的,右手戴左手套、右脚穿左鞋都会及其别扭和难受.
  • 3.即时树种的某一个节点只有一棵子树,也要区分他是左子树还是右子树.


二叉树的五种基本形态:

二叉树的五种基本形态


特殊二叉树


对于特殊二叉树,我只想谈一下满二叉树和完全二叉树,因为这两种二叉树容易发生混乱.

满二叉树:

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

下面我们看一下满二叉树和普通的二叉树的区别以及构成


满二叉树与普通二叉树.jpg
满二叉树的特点:
  • (1) 满二叉树的叶子只能出现在最下一层,出现在其它层就不可能达成平衡.
  • (2) 非叶子节点的度一定是2.
  • (3) 在同样深度的热茶树中,满二叉树的节点个数最多,叶子数最多.


完全二叉树:

其实学习完全二叉树要与满二叉树对比着看,首先我们先看一下完全二叉树的定义

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

满二叉树与完全二叉树
完全二叉树的特点:
  • (1) 完全二叉树的叶子节点只能出现在最下面的两层.
  • (2) 最下层的叶子一定集中在左部的连续位置.
  • (3) 倒数二层,若有叶子节点,则一定在右部的连续位置/
  • (4) 如果结点度为1,则该结点只有有孩子,即不存在只有右子树的情况.
  • (5) 同样结点书的二叉树,完全二叉树的深度最小.


完全二叉树与满二叉树的区别:

看完上面的完全二叉树和满二叉树的定义和特点,可能对两种特殊的二叉树有所混淆,所以我们来区分一下两种二叉树,首先从字面上区分,"完全"和"满"的差异,满二叉树一定是一棵完全二叉树,但完全二叉树不一定是满的.其实,完全二叉树的所有节点与同样深度的满二叉树,他们按层序编号相同的结点,是一一对应的,这里有个关键词是按层序编号.


二叉树的性质


二叉树的性质在面试题中会经常提到并且使用它进行计算面试题中的题目.那么接下来,看一下二叉树都有一些什么的性质吧.

二叉树性质1
内容:在二叉树的第 i 层上有至多 2^( i-1) 个结点 (i >= 1).

如下图,
第一层是根节点,只有一个,所以是 2^(1-1) = 2^0 = 0.
第二层有两个,2^(2-1) = 2 ^ 1 = 2.
第三层有四个,2^(3-1) = 2 ^ 2 = 4.
所以通过数据归纳法的论证,很容易得到二叉树的第 i 层上至多有2(i -1) 个结点的结论.

二叉树基本图.jpg
二叉树性质2
内容: 深度为 k 的二叉树至多有 2^k - 1 个节点(k >= 1).

还是上面的图,深度为k的意思就是有K层的二叉树.
如果有一层,至多有1 = 2 ^ 1 -1 个结点.
如果有两层,至多有1 + 2 = 2^2 -1个结点.
如果有三层,至多有1 + 2 + 4 = 2^3 -1个结点.
所以通过数据归纳法的论证,很容易得到深度为 k 的二叉树至多有 2^k - 1 个节点(k >= 1)的结论.

二叉树性质3
内容:对于任意一棵二叉树T,如果其终端结点数为n0,度为2的结点书为n2,那么有n0 = n2 + 1;
(注:度的解释,一个结点有n个子结点,那么他就是度为n的结点)

终端结点其实就是叶子结点数,而一棵二叉树,除了叶子结点 外,剩下的就是度为1或2的结点数了.我们设n1为度1的节点数.则数的总结点数为 n = n1 +n2 +n0;
我们来看下图,这个二叉树的总结点数为10,它是由A,B,C,D等度为2的结点,F,G,I,J等度为0的叶子结点和E这个度为1的结点组成.总和为4 + 1 + 5 = 10;
再来看一下,下图的总的连接线数为9,用代数表达式就是分支总数= n -1 = n1 + n2 ,因为我们刚才有等式 n = n0 + n1 + n2 ; 所以可以推导出来 n0 +n1 +n2 -1 = n1 +2 * n2, 结论就是n0 = n2 + 1;



二叉树性质4
内容:具有n个结点的完全二叉树的深度为 [log (2) n] + 1 ([x] 表示不大于x的最大整数) .

一个完全二叉树的结点数一定少于等于同样读书的满二叉树的结点数2k-1,但一定多于2(k-1) -1;
即为2^(k-1) -1 < n <= 2^k-1;由于结点数为整数,所以我们可以简化不等式,所以 2^(k-1) <= n < 2^k,不等式两边去对数 k-1 <= log2n <k ,因此 k = [log (2) n] + 1.(注:k为深度)

二叉树性质5
内容:如果对一棵有 n 个结点的完全二叉树(其深度为[log (2) n] + 1) 的结点按层序编号(从第1层到第[log (2) n] + 1 层,每一层从左到右),对任一结点i (1 <= i <= n) 有:
  • 1.如果 i = 1 ,则结点i是二叉树的根,无双亲;如果i > 1,则其双亲是结点 [ i /2 ];
  • 2.如果2i > n,则结点i无左孩子(结点i为叶子结点);否则其左孩子是结点2i;
  • 3.如果2i +1 > n,则结点i无右孩子;否则其右孩子是结点2i +1;

我们还是以下面这一棵二叉树进行验证,通过对性质5的一一比对,发现性质5是正确的,这里我就不一一比对了.



二叉树的遍历


二叉树的遍历多种多样,这里我只说,三种常用的遍历,前序遍历,中序遍历,后续遍历.我们在面试当中会遇到当量的二叉树遍历的问题.

前序遍历

####### 内容: 前序遍历首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树。
#######若二叉树为空则结束返回,否则:

  • #######(1)访问根结点。
  • #######(2)前序遍历左子树。
  • #######(3)前序遍历右子树 。
前序遍历

那么接下来,我们看一下用C语言改如果对一棵二叉树进行前序遍历的.
首先,我们使用链表对结点进行定义,并对二叉树进行创建.

typedef struct _BiTNode  
{  
    int data;  
    _BiTNode *leftChild;  
    _BiTNode *rightChild;  
}BiTNode, *pBiTree;  

//创建二叉树, 先序顺序  
int CreateBiTree(pBiTree *root)  
{  
    char ch = 0;  
    fflush(stdin);  
    if ((ch = getchar()) == 'a')//控制树的结构  
    {  
        *root = NULL;  
    }  
    else  
    {  
        *root = (BiTNode *)malloc(sizeof(BiTNode));  
        if (!(*root))  
        {  
            return RET_ERROR;  
        }  
        (*root)->data = GetRandom();  
        CreateBiTree(&(*root)->leftChild);  
        CreateBiTree(&(*root)->rightChild);  
    }  
    return RET_OK;  
}  
  

创建完成二叉树之后,我们使用递归的写一下前序遍历

//前序遍历  
void preOrder(struct BiTNode *bt)
{
if(bt != NULL){
          printf("%c ", bt->data); /* 访问根结点 */
          preOrder(bt->left); /* 前序遍历左子树 */
          preOrder(bt->right); /* 前序遍历右子树 */
       }
          return;
}

中序遍历
内容:中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。在遍历左、右子树时,仍然先遍历左子树,再访问根结点,最后遍历右子树。即:
若二叉树为空则结束返回,否则:
  • (1)中序遍历左子树。
  • (2)访问根结点。
  • (3)中序遍历右子树。
中序遍历

我们还是使用递归的思想来进行遍历,不解释,直接上代码

void inOrder(struct BTreeNode *bt){
   if(bt != NULL){
       inOrder(bt->left); /* 中序遍历左子树 */
        printf("%c ", bt->data); /* 访问根结点 */
       inOrder(bt->right); /* 中序遍历右子树 */
      }
       return;
   } 
后序遍历
内容:后序遍历首先遍历左子树,然后遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。即:

若二叉树为空则结束返回,否则:

  • (1)后序遍历左子树
  • (2)后序遍历右子树
  • (3)访问根结点
后序遍历

直接上代码..

void inOrder(struct BTreeNode *bt){
   if(bt != NULL){
       inOrder(bt->left); /* 后序遍历左子树 */
       inOrder(bt->right); /* 后序遍历右子树 */
        printf("%c ", bt->data); /* 访问根结点 */
      }
       return;
  } 

关于数据结构中的二叉树相关知识 就给大家说到这,谢谢大家,希望您能喜欢.

推荐阅读更多精彩内容

  • 数据结构和算法--二叉树的实现 几种二叉树 1、二叉树 和普通的树相比,二叉树有如下特点: 每个结点最多只有两棵子...
    sunhaiyu阅读 3,428评论 0 13
  • 第一章 绪论 什么是数据结构? 数据结构的定义:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 第二章...
    SeanCheney阅读 2,741评论 0 17
  • 1.树(Tree): 树是 n(n>=0) 个结点的有限集。当 n=0 时称为空树。在任意一颗非空树中:有且仅有一...
    ql2012jz阅读 285评论 0 3
  • 四、树与二叉树 1. 二叉树的顺序存储结构 二叉树的顺序存储就是用数组存储二叉树。二叉树的每个结点在顺序存储中都有...
    MinoyJet阅读 748评论 0 7
  • 1.树的定义 树是n(n>=0)个结点的有限集.n=0时称为空树.在任意一颗非空树种:(1)有且仅有一个特定的称为...
    蓝_上海求职阅读 1,069评论 1 13