数据结构之二叉树(遍历、建立、深度)

1、二叉树的深度遍历

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

        对于二叉树的深度遍历,有前序遍历二叉树、中序遍历二叉树、后序遍历二叉树三种形式,下面分别进行学习和介绍。

1.1 二叉树的前序遍历

        1)前序递归遍历

        规则是若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。如下图所示,遍历的顺序为ABDGHCEIF

 

前序递归遍历的代码实现,如下所示。

1

2

3

4

5

6

7

8

9

10

//前序递归遍历

void PreOrderTraverse(BiTree t)

{

    if(t != NULL)

    {

        printf("%c ", t->data);

        PreOrderTraverse(t->lchild);

        PreOrderTraverse(t->rchild);

    }

}

 

1.2 中序遍历二叉树

        1)中序递归遍历

      规则是若树为空,则空操作返回,否则从根结点开始(注意这里并不是先访问根结点),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。如下图所示,遍历的顺序为:GDHBAEICF

   

    中序递归遍历代码实现如下所示。

1

2

3

4

5

6

7

8

9

10

//中序递归遍历

void InOrderTraverse(BiTree t)

{

    if(t != NULL)

    {

        InOrderTraverse(t->lchild);

        printf("%c ", t->data);

        InOrderTraverse(t->rchild);

    }

}

 

1.3 后序遍历二叉树

        1)后序递归遍历

       规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。遍历的顺序为:GHDBIEFCA

        后序递归遍历代码实现如下所示。

1

2

3

4

5

6

7

8

9

10

//后序递归遍历

void PostOrderTraverse(BiTree t)

{

    if(t != NULL)

    {

        PostOrderTraverse(t->lchild);

        PostOrderTraverse(t->rchild);

        printf("%c ", t->data);

    }

}

 

     

  2、二叉树的广度遍历 

   广度遍历二叉树(即层次遍历)是用队列来实现的,从二叉树的第一层(根结点)开始,自上而下逐层遍历;在同一层中,按照从左到右的顺序对结点逐一访问。如下图所示,遍历的顺序为:ABCDEFGHI。

   

    按照从根结点到叶结点、从左子树到右子树的次序访问二叉树的结点,具体思路如下:

A. 初始化一个队列,并把根结点入队列;

B. 当队列为非空时,循环执行步骤3到步骤5,否则执行步骤6;

C. 出队列取得一个结点,访问该结点;

D. 若该结点的左子树为非空,则将该结点的左子树入队列;

E. 若该结点的右子树为非空,则将该结点的右子树入队列;

F. 结束。

广度遍历二叉树代码实现,如下所示。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

//层次遍历二叉树 - 广度遍历二叉树 - 队列

int TraverseBiTree(BiTree t)

{

    LinkQueue q;

    InitQueue(&q);

     

    BiTree tmp = t;

    if(tmp == NULL)

    {

        fprintf(stderr, "the tree is null.\n");

        return ERROR;

    }

 

    InsertQueue(&q, tmp);

    while(QueueIsEmpty(&q) != OK)

    {

        DeQueue(&q, &tmp);

        printf("%c ", tmp->data);

        if(tmp->lchild != NULL)

        {

            InsertQueue(&q, tmp->lchild);

        }

        if(tmp->rchild != NULL)

        {

            InsertQueue(&q, tmp->rchild);

        }

    }

 

    return OK;

}

 

3、推导遍历结果

两个二叉树遍历的性质:

前序遍历和中序遍历,可以唯一确定一棵树。

后续遍历和中序遍历,可以唯一确定一棵树。(这一块多做练习即可)

4、二叉树的建立

   如果要在内存中建立一个如下左图这样的树,wield能让每个结点确认是否有左右孩子,我们对它进行扩展,变成如下右图的样子,也就是将二叉树中的每个结点的空指针引出一个虚结点,其值为一个特定值,比如#,称之为扩展二叉树。扩展二叉树就可以做到一个遍历序列确定一棵二叉树了。如前序遍历序列为AB#D##C##。

    有了这样的准备,就可以看看如何生成一棵二叉树了。假设二叉树的结点均为一个字符,把刚才前序遍历序列AB#D##C##用键盘挨个输入,实现的算法如下所示。

二叉树建立实现代码一,如下所示。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

//创建树

//按先后次序输入二叉树中结点的值(一个字符),#表示空树

//构造二叉链表表示的二叉树

BiTree CreateTree(BiTree t)

{

    char ch;

    scanf("%c", &ch);

 

    if(ch == '#')

    {

        t = NULL;

    }

    else

    {

        t = (BitNode *)malloc(sizeof(BitNode));

        if(t == NULL)

        {

            fprintf(stderr, "malloc() error in CreateTree.\n");

            return;

        }

 

        t->data = ch;                        //生成根结点

        t->lchild = CreateTree(t->lchild);    //构造左子树

        t->rchild = CreateTree(t->rchild);    //构造右子树

    }

    return t;

}

 

    二叉树建立实现代码二,如下所示。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

//创建树方法二

int CreateTree2(BiTree *t)

{

    char ch;

    scanf("%c", &ch);

 

    if(ch == '#')

    {

        (*t) = NULL;

    }

    else

    {

        (*t) = (BiTree)malloc(sizeof(BitNode));

        if((*t) == NULL)

        {

            fprintf(stderr, "malloc() error in CreateTree2.\n");

            return ERROR;

        }

 

        (*t)->data = ch;

        CreateTree2(&((*t)->lchild));

        CreateTree2(&((*t)->rchild));

    }

    return OK;

}

 

    其实建立二叉树,也是利用了递归的原理。只不过在原来应该打印结点的地方,改成生成结点、给结点赋值的操作而已。因此,完全可以用中序或后序遍历的方式实现二叉树的建立,只不过代码里生成结点和构造左右子树的代码顺序交互一下即可。

5、二叉树的深度

树中结点的最大层次称为树的深度。对于二叉树,求解树的深度用以下两种方法实现。即非递归和递归的方法实现。求二叉树的深度也是非常常见的一个操作。这个操作使用后续遍历比较符合人们求解二叉树高度的思维方式:首先分别求出左右子树的高度,在此基础上得出该棵树的高度,即左右子树较大的高度值加1.

递归求解二叉树的深度实现代码,如下所示。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

//二叉树的深度 - 递归

//返回值: 二叉树的深度

int tree_height(BiTree t)

{

    int dept = 0;

     

    if(t)

    {

        int lchilddept = BiTreeDeep(t->lchild);

        int rchilddept = BiTreeDeep(t->rchild);

 

        dept = lchilddept >= rchilddept ? (lchilddept + 1) : (rchilddept + 1);

    }

 

    return dept;

}

 

6、统计二叉树叶子节点

二叉树的遍历是操作二叉树的基础,二叉树的很多特性都可以通过遍历二叉树得到。统计二叉树叶子节点的个数常见的操作。

/*统计二叉树中的叶子结点数*/

/**************************************************

算法描述:编写递归算法,计算二叉树中叶子节点数目 

****************************************************/

 

int leaf_num(ptnode list)

{

  if(NULL == list) 

  {

return 0;  //空树,无叶子

  }

  else if(!list->pLchild && !list->pRchild)

  {

 return 1; 

  }

  else 

  {

return (leaf(list->pLchild) + leaf(list->pRchild));

  }

}

/************************

算法分析:

首先要明白其本质还是二叉树的遍历.只不过是带额外有条件的输出.即找出叶子结点并

进行计数.

1.提到计数的话,一开始的反应就是建立一个int型变量(如count),然后找到一个符合条件

的就进行count ++; 但在这里就不是那么合适了.因为若是要遍历,选择递归遍历,则每次

调用一次递归函数都会创建一个count,各个count都不相同.而且都会被初始化为0,这样就

没什么意义了.

2.所以采取的方法就是利用函数返回值.把函数定义为返回值为int型的函数.

3.然后进行判断:

if(!t->lch && !t->rch)如果左右结点都为NULL,则返回1(也就是计数+1).

否则就调用递归函数,先左子树,后右子树.这个算法真正精髓的一句就是:

return (leaf(t->lch) + leaf(t->rch));

在调用递归的同时把各个递归函数的返回值都加了起来.而最终返回到主函数的值

就是叶子节点的个数!巧妙! 

**************************/

如果需要完整程序,可找我索取。

出自:《大话数据结构》严蔚敏老师之《数据结构》

二叉树的算法我大都是利用递归实现的,非递归的后面用到再进行深究。二叉树自己还不是十分理解,以后还应该进行加强巩固。

 

   

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值