数据结构系列--树

 递归背后栈的内部机制。

    之前所说过的数据结构都是一对一,即一个位于该数据结构中的数据它有一个前驱和一个后继(或者没有)。而在树中一个节点只有一个前驱,但是却有多个后继。树虽然是非线性的逻辑结构但是它依然可以用线性结构来存储。


     树(英语:tree)是一种抽象数据类型或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,根朝上,而叶朝下的。它具有以下的特点:

    ①每个节点有零个或多个子节点;
    ②没有父节点的节点称为根节点;
    ③每一个非根节点有且只有一个父节点;
    ④除了根节点外,每个子节点可以分为多个不相交的子树;

明)。

    树的一系列术语:度,叶子节点,根节点,父节点,子节点,深度,高度。


二叉树

 

    每个节点最多含有两个子树的树称为二叉树。(我们一般在书中试题中见到的树是二叉树,但并不意味着所有的树都是二叉树。)

在二叉树的概念下又衍生出满二叉树和完全二叉树的概念

满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点。也可以这样理解,除叶子结点外的所有结点均有两个子结点。节点数达到最大值,所有叶子结点必须在同一层上

在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。


完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1~(h-1)层) 的结点数都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。(最后一层缺子树)

满二叉树一定是完全二叉树,但反过来不一定成立。

二叉树性质:

  1. 在二叉树的第i层上最多有2i-1个节点 (i>=1)

  2. 二叉树中如果深度为k,那么最多有2k-1个节点。(k>=1)

  3. n0=n2+1,其中n0表示度数为0的节点数,n2表示度数为2的节点数。

  4. 在完全二叉树中,具有n个节点的完全二叉树的深度为[log2n]+1,其中[log2n]是向下取整

  5. 若对含 n个结点的完全二叉树从上到下从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为i的结点有如下特性:

  • i=1,则该结点是二叉树的根,无双亲, 否则,编号为[i/2]的结点为其双亲结点;

  • 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;

  • 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1的结点为其右孩子结点


 

二叉树储存

顺序存储

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

当二叉树为完全二叉树时,结点数刚好填满数组。当二叉树不为完全二叉树时,采用顺序存储形式出现了空间浪费的情况。对于这种右斜树极端情况,采用顺序存储的方式是十分浪费空间的。因此,顺序存储一般适用于完全二叉树

二叉链表

采用链式存储存储二叉树,这种链表称为二叉链表。由二叉树定义可知,二叉树的每个结点最多有两个孩子。因此,可以将结点数据结构定义为一个数据两个指针域


二叉树遍历

先序遍历:先根节点->遍历左子树->遍历右子树。从二叉树的根结点出发,当第一次到达结点时就输出结点数据,按照先向左再向右的方向访问。

中序遍历:遍历左子树->根节点->遍历右子树。从二叉树的根结点出发,当第二次到达结点时就输出结点数据,按照先向左再向右的方向访问。

后序遍历:遍历左子树->遍历右子树->根节点。从二叉树的根结点出发,当第三次到达结点时就输出结点数据,按照先向左再向右的方向访问。


二叉树遍历应用

 

经过遍历把非线性结构变成线性结构

1 输出二叉树中的节点

2 输出二叉树的叶子节点

3 统计叶子结点数目

4 建立二叉链表方式存储的二叉树

5 求二叉树高度

6 按树形状打印的二叉树

 

逆中序便利实现横向树状打印

void PrintTree(TreeNode Boot,int nLayer)
{
  if(Boot == NULL)
  RETURN;
  PrintTree(Boot->RClild,nLayer+1);
  // 访问根结点的操作
  for(int i = 0;i<nLayer;i++)
  printf(" " );
  printf("%c/n",Boot->ch);
  PrintTree(Boot->LChild,nLayer+1);// 递归调用遍历左子树,层次值和打印位置有关
}

 

思考题:可用何种便利实现二叉树的左右子树交换?

先序后序都可以,中序不行,根结点在中间,是一种对称性的遍历,左子树遍历结束之后已经交换,再遍历右子树(此时右子树是原来的左子树)会回到原结果

 

递归遍历和消除是最重要的两部分

基于栈的递归消除

递归转到非递归的原因:

    递归执行效率很低

    运行环境没有递归机制

 

递归转化的两种方法:

    直接尾递归:可化为直线型,用循环替代递归

    复杂情况:递归问题无法直接转换成循环,采用工作栈消除递归

将递归中系统隐含的栈-》用户自己操纵的栈(工作栈提供一种控制结构)

递归进层:

    1 保留本层参数,返回地址

      2 给下层参数赋值,传递参数,分配局部数据空间

      3  控制转向入口,新层开始

递归退层:

    1 恢复上层

      2 传递结果

      3 转断点执行

while遍历左子树,两个大循环相套

p当前结点q直接前驱,控制L访问完之后该去右子树,如果没有右子树访问根

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值