算法导论 10-4-5 O(n)时间 O(1)空间遍历树

本文详细阐述了如何通过非递归方式实现树的中序遍历,利用栈结构来保存结点之间的联系,确保访问顺序正确且避免重复输出结点关键信息。通过分析结点之间的父子、兄弟关系,实现从左到右的遍历过程,并在遍历左子树后输出结点,在遍历右子树前返回并访问父结点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了遍历整棵树,我们得保存结点之间的联系才能进行跳转,访问某个子结点时,需要考虑的因素有:

  1. 它有可能是某个结点的左孩子结点,也有可能是其右孩子结点;
  2. 如果这个结点是在树(包括子树)的左部,我们如何自底向上的找到根结点以访问树的右部,同时又访问了其后代结点;
  3. 结点的关键值输出放在什么位置保证只该结点只输出一次。

非递归和有限的额外存储空间是这个问题的难点。

首先一点很明确,访问结点有先后关系,通常遍历都是从根结点开始自上而下的。由上面的分析可知道,无论怎么访问,都得考虑从当前正在访问的结点返回到其父亲结点上。也就是说,我们可以考虑一种类似“中序”遍历的思想:访问某个结点,马上访问其左孩子子树,接着输出自己的值,再访问其右孩子子树,最后借助自己返回到父亲结点。

其次要考虑的就是怎么在访问某个结点的后代结点时,还能够“回去访问”该结点的祖先结点。这就要利用当前正在访问的结点和前一个访问的结点之间的关系了。当前结点和前一个已访问的结点不外乎有三种关系:

  1. 如果已访问结点是当前访问的结点的父亲,说明我们刚刚移动到这个结点,接下来要继续访问它的左孩子结点;
  2. 如果已访问结点是当前访问的结点的左孩子,说明当前节点的左孩子已经被访问,需要继续要访问它的右孩子;
  3. 如果已访问结点是当前访问结点的右孩子,说明当前结点的两棵子树均已遍历完毕,下一步直接访问其父亲节点。
总结一下:其实就是根据最近访问的两个结点的关系决定下一个访问结点;结点的关键值在访问左子树之后、访问右子树之前输出即可。

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
struct TreeNode
{
    int key;
    struct TreeNode * parent;
    struct TreeNode * left;
    struct TreeNode * right;
    TreeNode()
    {
        parent = NULL;
        left = NULL;
        right = NULL;
    }
};
TreeNode* createTree(TreeNode * root,int d);
void printTreeF(TreeNode * root);
void printTreeM(TreeNode * root);
void printTreeNONRECURiSION(TreeNode *root);

int main ()
{
    TreeNode*root = 0;
    srand(time(NULL));
    int n = 13;
    for (int i = 0;i < n;i ++)
    {
        root = createTree(root,rand());
    }
    printTreeF(root);
    cout <<endl;
    printTreeM(root);
    cout << endl;
    printTreeNONRECURiSION(root);
}
TreeNode* createTree(TreeNode * root,int d)
{
    struct TreeNode * tmp = new struct TreeNode();
    tmp->key = d;
    if (NULL == root)
    {
        root = tmp;
        return root;
    }
    else
    {
        struct TreeNode* p = NULL;
        struct TreeNode* q = NULL;
        p = root;
        while (p != NULL)
        {
            q = p;
            if (d < p->key )
            {
                p = p->left;
            }
            else
            {
                p = p->right;
            }
        }
        if (d < q->key)
        {
            q->left = tmp;
            tmp->parent =q;
        }
        else if ( d > q->key)
        {
            q->right = tmp;
            tmp->parent =q;
        }
        else
        {
            delete tmp;
        }
        return root;
    }
}

void printTreeF(TreeNode * root)
{
    if (NULL == root)
        return;
    cout << root->key << '\t';
    printTreeF(root->left);
    printTreeF(root->right);
}

void printTreeM(TreeNode * root)
{
    if (NULL == root)
        return;
    printTreeM(root->left);
    cout << root->key << '\t';
    printTreeM(root->right);
}
void printTreeNONRECURiSION(TreeNode *root)
{
    if (NULL == root)
    {
        return;
    }
    TreeNode * pre = root;
    TreeNode * cur = NULL;
    TreeNode * next = NULL;
    if (pre->left)
    {
        cur = pre->left;
    }else if (pre->right)
    {
        cur = pre->right;
    }
    if (NULL == cur)
    {
        cout << pre->key << ' ';
        return;
    }
    while (cur != NULL)
    {
        if ( pre == cur->parent)
        {
           next = cur->left;
           if (NULL == next)
           {
               cout << cur->key << ' ';
               next = cur->right;
               if (NULL == next)
               {
                   next = cur->parent;
               }
           }
        }
        else
        {
            if (pre == cur->left)
            {
                cout << cur->key << ' ';
                next = cur->right;
                if(NULL == next)
                {
                    next = cur->parent;
                }
            }
            else
            {
                next = cur->parent;
            }
        }
        pre = cur;
        cur = next;
    }

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值