【算法题】二叉树的前序遍历(递归和非递归算法分析)

来源:互联网 发布:java上传图片到tomcat 编辑:程序博客网 时间:2024/05/18 06:24
          问题:对二叉树的前序遍历(递归和非递归算法)

递归算法输出根节点的值
         对左子树进行左遍历
         对右子树进行遍历

  代码如下:
void PreorderTraversal(node *root){  if(root)   printf("%d\n",root->value);elsereturn ;PreorderTraversal(root->left);PreorderTraversal(root->right);}

这个算法的执行时间是多少呢?因为每个节点被检查了一次,所以它是O(n)级的。

        非递归算法:要完成该算法,需要深入分析一下递归算法的本质,再用循环算法来模拟它的动作。递归算法使用了堆栈这种数据结构,利用堆栈来完成数据处理。
堆栈的四个函数:

int Push(element **stack,void *data);
int Pop(element **stact,void **data);
int CreateStack(element **stack);
int deleteStack(element **stack);


首先来探究下递归算法的本质:
递归算法:a.输出根节点的值
          b.对左子树进行左遍历
          c.对右子树进行遍历


           第一次进入递归函数的时候,先输出根节点的值,然后递归地调用这个函数对左子树进行遍历。在发出这个递归调用的时候,调用者的程序状态就会被压入堆栈保存起来,这样在递归调用返回时,调用者就能从自己离开的地方开始继续执行。
         对左遍历算法来说,调用者将继续对右子树进行左遍历。为了能在遍历完左子树之后开始对右子树的遍历,递归调用隐含地把右子树的地址保存在堆栈上。在输出一个节点之后但在前进到它的左节点之前,该节点的右节点将被压入一个你不知道的堆栈里保存起来。当没有子节点可供处理时,就会从递归调用中返回一层,从堆栈上弹出一个右节点,然后以它为起点继续进行左遍历。
         总结:这个算法先输出当前节点的值,然后再把它的右节点压入堆栈,然后前进到它的左节点;当没有子节点可供处理时,算法将从堆栈上弹出一个新的当前节点。这个过程将一直重复到遍历完所有的节点为止,堆栈里也不再有任何数据。
 
 注:必须保证左节点总是先于右节点被弹出来,所以得先压入右节点,再压入左节点。


  非递归算法如下:
    创建堆栈
     把根节点压入堆栈
    当堆栈不为空,循环
     弹出一个节点
     如果这个节点不是NULL
       输出它的值
       把这个节点的右节点压入堆栈
       把这个节点的左节点压入堆栈


 代码如下:
 
 void PreorderTraversal(node *root){element *theStack;void *data;node *curNode;CreateStack(&theStack);Push(&theStack,root);while(Pop(&theStack,&data)){curNode=(node *)data;if(curNode){printf("%d\n",curNode->value);Push(&theStack,curNode->right);Push(&theStack,curNode->left);}}DeleteStack(&theStack);}

这个算法的时间开销也是O(n)