二叉树非递归先序遍历

二叉树的先序遍历使用递归很简单,代码如下:

void preOrder(TNode* root)
{
if (root != NULL)
{
Visit(root);
preOrder(root->left);
preOrder(root->right);
}
}

先访问跟节点,然后访问左节点,再访问右节点。如果不用递归,那该怎么做呢?仔细看一下递归程序,就会发现,其实每次都是走树的左分支(left),直到左子树为空,然后开始从递归的最深处返回,然后开始恢复递归现场,访问右子树。

其实过程很简单:一直往左走 root->left->left->left…->null,由于是先序遍历,因此一遇到节点,便需要立即访问;由于一直走到最左边后,需要逐步返回到父节点访问右节点,因此必须有一个措施能够对节点序列回溯。有两个办法:

1.用栈记忆:在访问途中将依次遇到的节点保存下来。由于节点出现次序与恢复次序是反序的,因此是一个先进后出结构,需要用栈。

2.节点增加指向父节点的指针:通过指向父节点的指针来回溯

方法一,直接用栈模拟递归。次将节点压入栈,然后弹出,压右子树,再压入左子树,在遍历过程中,遍历序列的右节点依次被存入栈,左节点逐次被访问。同一时刻,栈中元素为m-1个右节点和1个最左节点,最高为h。所以空间也为O(h);每个节点同样被压栈一次,弹栈一次,访问一次,时间复杂度O(n)

01#include <stdlib.h>
02#include <stdio.h>
03#include <iostream>
04#include <stack>
05 
06 using namespace std;
07 
08/* 二叉树节点 */
09 struct node
10{
11     int data;
12     struct node* left;
13     struct node* right;
14};
15 
16 struct node* newNode(int data)
17{
18     struct node* node = new struct node;
19     node->data = data;
20     node->left = NULL;
21     node->right = NULL;
22     return(node);
23}
24 
25// 非递归的方式先序遍历二叉树
26 void iterativePreorder(node *root)
27{
28     if (root == NULL)
29        return;
30 
31     // 根节点入栈
32     stack<node *> nodeStack;
33     nodeStack.push(root);
34 
35     while (nodeStack.empty() == false)
36     {
37         // 访问根节点
38         struct node *node = nodeStack.top();
39         printf ("%d ", node->data);
40         nodeStack.pop();
41 
42         // 不为空的话则入栈
43         if (node->right)
44             nodeStack.push(node->right);
45         if (node->left)
46             nodeStack.push(node->left);
47     }
48}
49 
50//测试
51 int main()
52{
53     /*  创建以下的树
54             10
55           /   \
56         8      2
57       /  \    /
58     3     5  2
59   */
60   struct node *root = newNode(10);
61   root->left        = newNode(8);
62   root->right       = newNode(2);
63   root->left->left  = newNode(3);
64   root->left->right = newNode(5);
65   root->right->left = newNode(2);
66   iterativePreorder(root);
67   return 0;
68}

 方法二 同样是使用栈,每次都将遇到的节点压入栈,当左子树遍历完毕后才从栈中弹出最后一个访问的节点,访问其右子树。在同一层中,不可能同时有两个节点压入栈,因此栈的大小空间为O(h),h为二叉树高度。时间方面,每个节点都被压入栈一次,弹出栈一次,访问一次,复杂度为O(n)

01 void iterativePreorder2(node *root) {
02     stack<node *> s;
03     while ((root != NULL) || !s.empty()) {
04         if (root != NULL) {
05             printf("%d ", root->data);
06             s.push(root);       // 先序就体现在这里了,先访问,再入栈
07             root = root->left;  // 依次访问左子树
08         else {
09             root = s.top();     // 回溯至父亲节点. 此时根节点和左子树都已访问过
10             s.pop();
11             root = root->right;
12         }
13     }
14}

原文:http://www.acmerblog.com/iterative-preorder-traversal-5853.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 以下是二叉树非递归先序遍历的流程图: ![二叉树非递归先序遍历流程图](https://img-blog.csdn.net/20180503173815601?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xlZUxhc3Qx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/q/50) 具体步骤如下: 1. 初始化一个空栈和当前节点为根节点。 2. 当前节点不为空时,输出当前节点的值,并将其入栈,然后将当前节点更新为它的左子节点。 3. 如果当前节点为空,弹出栈顶元素并将当前节点更新为它的右子节点。 4. 重复步骤2和步骤3,直到栈为空且当前节点为空。 ### 回答2: 二叉树非递归先序遍历可以通过使用栈来实现。下面是该算法的流程图: 1) 创建一个空栈,将根节点压入栈中。 2) 当栈不为空时,执行以下步骤: a) 弹出栈顶元素,并输出其值。 b) 如果该节点的右子树不为空,则将右子树的根节点压入栈中。 c) 如果该节点的左子树不为空,则将左子树的根节点压入栈中。 3) 重复步骤2,直到栈为空。 简单解释一下流程图中的每个步骤: 1) 首先创建一个空栈,并将根节点压入栈中,这是遍历的起始点。 2) 进入循环,栈不为空时执行步骤。 a) 弹出栈顶元素,并输出其值。这表示我们正在处理一个节点,并将其值进行输出。 b) 如果该节点有右子树,则将右子树的根节点压入栈中。这是因为我们希望先处理左子树,所以需要将右子树推迟到左子树处理完之后再处理。 c) 如果该节点有左子树,则将左子树的根节点压入栈中。这是因为左子树在栈中的位置会先于右子树,所以需要先处理左子树。 3) 重复步骤2,直到栈为空。这是循环的终止条件,表示遍历已经完成。 通过上述流程,我们可以实现对二叉树非递归先序遍历。这种遍历方法的时间复杂度为O(n),其中n是二叉树中节点的数量。 ### 回答3: 二叉树非递归先序遍历可以使用栈来实现。下面是二叉树非递归先序遍历的流程图: 1. 创建一个空栈,用于保存待遍历的节点。 2. 将根节点入栈。 3. 循环执行以下步骤直到栈为空: - 弹出栈顶节点,将其值输出。 - 如果该节点的右子节点不为空,则将右子节点入栈。 - 如果该节点的左子节点不为空,则将左子节点入栈。 4. 重复步骤3,直到栈为空。 例如,对于如下的二叉树: ``` A / \ B C / \ \ D E F ``` 首先将根节点A入栈,然后循环执行步骤3: - 弹出栈顶节点A,输出A的值。 - A的右子节点C入栈。 - A的左子节点B入栈。 接下来继续循环执行步骤3: - 弹出栈顶节点B,输出B的值。 - B的右子节点E入栈。 - B的左子节点D入栈。 继续循环执行步骤3: - 弹出栈顶节点D,输出D的值。 - D没有右子节点,也没有左子节点,栈为空,结束循环。 最后的输出序列为A、B、D、E、C、F。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值