在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树、二叉堆、和哈夫曼树。

二叉树的遍历是指依照某种顺序逐一访问二叉树的所有结点。这里的“访问”可以是输出结点中的数据,也可以是对结点进行某种处理或操作。遍历操作本着“不重不漏”的原则,即每个结点只能访问一次,且全部结点都能访问到。

为什么需要二叉树遍历呢?线性数组或链表可以单向从头到尾遍历或反向遍历。二叉树是非线性数据结构,通过遍历可以将二叉树中的结点访问一次且仅一次,从而得到访问结点的顺序序列。从这个意义上说,遍历操作就是将二叉树中结点按一定规律线性化的操作,目的在于将非线性化结构变成线性化的访问序列。

二叉树遍历有深度优先遍历和广度优先遍历二种思想。

1 深度优先的递归遍历

从二叉树的定义可知,一棵二叉树宏观上由三部分组成:根结点、根结点的左子树和根结点的右子树。因此只要完整地遍历了这三部分,就等于遍历了整棵二叉树。二叉树的根结点可以通过指针直接访问到根结点的数据。但是如何遍历根结点的左子树和右子树呢?其实可以把根结点的左子树和右子树看作两棵独立的二叉树,以同样的方式来遍历左子树和右子树显然这是一种递归形式的遍历。

二叉树通常有3种遍历方法(先左后右):

先序(根)遍历(DLR,根左右)

中序(根)遍历(LDR,左根右)

后序(根)遍历(LRD,左右根)

其中D表示根节点、L表示左子树,R表示右子树。因为二叉树结构本身就是一种递归的结构,所以这3种遍历方式也都是递归形式定义的。先序遍历DLR表示D→L→R,如下:

void PreOrderTraverse(BiTreee T){

if(T){ //如果二叉树为空,递归遍历结束

visit(T); //访问根节点,可以是输出或其它处理

PreOrderTraverse(T->lchild); //先序遍历T的左子树

PreOrderTraverse(T->rchild); //先序遍历T的右子树

}

}

其它两种遍历只是visit(T);语句放置的位置不一样。所以三种递归遍历算法的搜索路线相同,只是访问各节点的时间不同。

具体线路为: 从根结点出发,逆时针沿着二叉树外缘移动,对每个结点均途径三次,最后回到根结点。(逆时钟方向,整体上是从左子树到右子树,从内到外)

(三种递归相同的搜索路线,类似于斐波那契的递归算法。是一种函数的递归调用,是一个调用栈对函数和参数在栈中的压入与弹出形成的路线,相关内容请参考:C|深入了解函数调用与递归调用(递推、回归))

任一节点都有三次访问的机会,如下图所示:

作为根节点被调用(在上图中用箭头↙或↘表示);

从左子树返回(如果是叶结点,则是从空左子树返回,用箭头↗表示);

从右子树返回(如果是叶结点,则是从空右子树返回,用箭头↖表示);

对于先序遍历来说,剪头第一次经过的结点(递归调用第一时间访问根节点),就是遍历的序列,以后再次经历就不算进去了。

对于中序遍历来说,剪头第二次经过的结点(左子树返回后访问根节点),就是遍历的序列,之前的和以后的再次经历就不算进序列里去了。

对于后序遍历来说,剪头第三次经过的结点(右子树返回后访问根节点),就是遍历的序列,之前经历的就不算进去了。

2 二叉树前序遍历的非递归实现

二叉树的遍历可以通过递归调用实现,也可以用非递归来实现。当用非递归来实现时,需要使用一个栈来辅助,用来记录尚待遍历的结点,以备以后访问。

二叉树的非递归前序遍历:对二叉树各结点的访问顺序是沿其左链一路访问下来,在访问结点的同时将其入栈,直到左链为空。然后结点出栈,对于每个出栈结点,即表示该结点和其左子树已被访问结束,应该访问该结点的右子树。具体步骤如下:

2.1 当前指针指向根结点;

2.2 打印当前结点,当前指针指向其右子树并进栈,重复操作,直到左子树为NULL;

2.3 依次退栈,将当前指针指向右子树;

2.4 苦栈非空或当前指针非NULL,执行2.2,否则结束。

总的思路就是从根结点开始,左子树遍历,并把左子结点推入栈中,然后出栈操作,同时遍历右子树。遍历的顺序上非递归遍历的顺序路线相同。通过双重循环实现。

3 编程实例

编程实现二叉树的创建和先序、中序、后序遍历及深度优先的非递归遍历:

代码:

运行结果:

需要注意的是,在CreatBiTree(BiTree *T)中,因为BiTree本身就是链表指针,所以*T中的T是一个指向指针的指针,T为指向BiTree类型的指针类型,也就是指向指针的指针,*T实质上是一个指针变量。故修改*T就修改了实参(根指针)本身 。

以上三种递归遍历的方法,如果将访问的语句去掉,也三种结构的语法相同,所以说,他们的路径是相同的,只是对根结点的访问时机不同。

深度优先的递归或非递归实现,都是从左往右,从外往内,遍历路线的顺序相同。递归在于编程语言使用了调用栈,非递归在于编程人员自己使用了一个辅助栈。

附源码:

#include ";

#define MAX 20

typedef struct BiTNode{

char data;/*结点的数据域*/

struct BiTNode *lchild , *rchild;/*指向左孩子和右孩子*/

} BiTNode , *BiTree;

void CreatBiTree(BiTree *T){/*按先序序列创建一棵二叉树*/

char c;

scanf("%c",&c);

if(c == ' ') *T = NULL;

else{

*T = (BiTNode * )malloc(sizeof(BiTNode));/*创建根结点*/

(*T)->data = c;/*向根结点中输入数据*/

CreatBiTree(&((*T)->lchild));/*递归地创建左子树*/

CreatBiTree(&((*T)->rchild));/*递归地创建右子树*/

}

}

void PreOrderTraverse(BiTree T ){/*先序遍历二叉树*/

if(T){/*递归结束条件,T为空*/

printf("%3c",T->data);/*访问根结点,将根结点内容输出*/

PreOrderTraverse(T->lchild);/*先序遍历T的左子树*/

PreOrderTraverse(T->rchild);/*先序遍历T的右子数*/

}

}

void InOrderTraverse(BiTree T){/*中序遍历二叉树*/

if(T){/*如果二叉树为空,递归遍历结束*/

InOrderTraverse(T->lchild);/*中序遍历T的左子树*/

printf("%3c",T->data);/*访问根结点*/

InOrderTraverse(T->rchild);/*中序遍历T的右子数*/

}

}

void PosOrderTraverse(BiTree T){/*后序遍历二叉树*/

if(T){/*如果二叉树为空,递归遍历结束*/

PosOrderTraverse(T->lchild);/*后序遍历T的左子树*/

PosOrderTraverse(T->rchild);/*后序遍历T的右子数*/

printf("%3c",T->data);/*访问根结点*/

}

}

void PreOrder_NR(BiTree T)//深度优先非递归前序遍历

{

BiTree Ptr;

BiTree Stack[MAX];//栈定义

int top=0;//栈顶指针

Ptr = T;

do

{

while(Ptr!=NULL)//树结点非空,遍历其左子树

{

printf("%3c",Ptr->data);//操作结点

Stack[top]=Ptr;//树结点进栈

top++;

Ptr=Ptr->lchild;//查看左子树

}

if(top>0)//栈非空,出栈

{

top--;

Ptr=Stack[top];

Ptr=Ptr->rchild;//取栈顶点结点右子树

}

} while(top>0 || Ptr!=NULL);

}

main()

{

BiTree T = NULL;/*最开始T指向空*/

printf("需要创建和遍历的二叉树:\n");

printf(" A\n");

printf(" / \\\n");

printf(" B E\n");

printf(" / \\ \\\n");

printf(" C D F\n");

printf("Input some characters to create a binary tree\n");

printf("(按先序序列建立)\n");

printf("例如,ABC##D##E#F##↙,#表示空格,↙表示回车\n");

CreatBiTree(&T);/*创建二叉树*/

printf("The squence of preorder traversaling(先序遍历) binary tree\n");

PreOrderTraverse(T);/*先序遍历二叉树*/

printf("\nThe squence of inorder traversaling(中序遍历) binary tree\n");

InOrderTraverse(T);/*中序遍历二叉树*/

printf("\nThe squence of posorder traversaling(后序遍历) binary tree\n");

PosOrderTraverse(T);/*后序遍历二叉树*/

printf("\n深度优先非递归前序遍历二叉树\n");

PreOrder_NR(T);

getchar(); getchar();

}

-End-

1.《[如何创建二叉排序树]创建二叉排序树的时间复杂度!》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

2.《[如何创建二叉排序树]创建二叉排序树的时间复杂度!》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/keji/3261140.html