二叉树的遍历 《算法导论》10.4-1~10.4-3 10.4-5

  • 10.4-1 那棵树就长成下面这个样子
  /*
            18
        12       10
      7    4   2    21
          5
    */
  • 下面就借用10.4-1提供的数据,构建一棵树,然后分别对它做10.4-2所要求的递归遍历和10.4-3所要求的非递归遍历。

    递归遍历的方式有三种,前序、中序、后序,实现上的差异,无非是把Traverse1(Index)函数里后三句的顺序换一换。

    非递归遍历,简单的算法是借助一个栈或者队列,同样有前序、中序、后序形式。复杂一些的算法就是下面的10.4-5。

  • 10.4-5要求实现一种非递归的遍历方法,且除了树本身外,只使用固定存储空间。

    反思一下为什么Traverse2需要使用O(n)的额外存储空间呢?那是因为一个Node出栈的同时,最多情况下,会向栈中压入左右孩子两个新节点,所以栈的空间是呈线性增长的。如果要限制存储空间为常量,就必须放弃“通过一个旧节点找到两个新节点”的做法,取而代之以“用一个旧节点找到一个新节点”的做法。

    于是就有了下面的问题,一方面,每个节点的职责是保证自己的两个孩子都被遍历到,另一方面,受到存储的限制,通过当前节点只可以找出一个新节点,,怎么办呢?只好先找左孩子,再通过左孩子找自己,然后再通过自己找右孩子(左右顺序无所谓,先右再左也一样)。所以,整个过程中当前节点指针的行走方向一共有三种状态,1.从父亲找到的自己;2. 从左孩子找到的自己 3. 从右孩子找到的自己。每种状态下当前节点该做的工作是不一样的。具体实现见Traverse3。

#include <iostream>
#include <stack>
using namespace std;

class BinaryTree
{
public:
    using Index = int;
    BinaryTree();

    //O(n)-time recursive traverse 10.4-2
    void Traverse1() const;
    void Traverse1(Index root) const;

    //O(n)-time nonrecursive traverse10.4-3
    void Traverse2() const;

    //O(n)-time nonrecursive traverse,with constant extra space 10.4-5
    void Traverse3() const;
private:
    struct Node
    {
        int key;
        Index parent;
        Index left;
        Index right;
    };

    Node m_array[11];
    Index m_root;
};

BinaryTree::BinaryTree()
{
    m_root = 6;
    m_array[1] = Node{12,6,7,3};
    m_array[3] = Node{4,1,10,-1};
    m_array[4] = Node{10,6,5,9};
    m_array[5] = Node{2,4,-1,-1};
    m_array[6] = Node{18,-1,1,4};
    m_array[7] = Node{7,1,-1,-1};
    m_array[9] = Node{21,4,-1,-1};
    m_array[10] = Node{5,3,-1,-1};
}

void BinaryTree::Traverse1() const
{
    Traverse1(m_root);
}
void BinaryTree::Traverse1(Index root) const
{
    if(root == -1)
        return;
    cout << m_array[root].key << "\t";
    Traverse1(m_array[root].left);
    Traverse1(m_array[root].right);
}

void BinaryTree::Traverse2() const
{
    stack<Index> indexStack;
    indexStack.push(m_root);
    int index,childIndex;
    while(!indexStack.empty())
    {
        index = indexStack.top();
        indexStack.pop();
        cout << m_array[index].key << "\t";
        childIndex = m_array[index].right;
        if(childIndex != -1)
            indexStack.push(childIndex);
        childIndex = m_array[index].left;
        if(childIndex != -1)
            indexStack.push(childIndex);
    }
}

void BinaryTree::Traverse3() const
{
    enum
    {
        Parent,
        Left,
        Right
    };
    int   come_from = Parent;
    Index index_next = m_root,index;
    bool  back_to_parent;
    while(true)
    {
        index = index_next;
        back_to_parent = false;
        if(come_from == Parent)        //从父亲找到自己,我的任务是打印自身,然后优先找可能存在的左孩子
        {
            cout << m_array[index].key << "\t";
            index_next = m_array[index].left;
            if(index_next == -1)
                index_next = m_array[index].right;
            if(index_next == -1)
                back_to_parent = true;
        }
        else if(come_from == Left)    //从左孩子返回到自己,我的任务是找可能存在的右孩子
        {
            index_next = m_array[index].right;
            come_from = Parent;
            if(index_next == -1)
                back_to_parent = true;
        }
        else                                    //从右孩子返回到自己,我的任务是返回到我的父亲
        {
            back_to_parent = true;
        }
        if(back_to_parent)
        {
            index_next = m_array[index].parent;
            if(index_next == -1)
                break;
            if(m_array[index_next].left == index)
                come_from = Left;
            else
                come_from = Right;
        }
    }

}

void Test()
{
    BinaryTree tree;
    tree.Traverse1(); //18	12	7	4	5	10	2	21
    cout << endl;
    tree.Traverse2(); //18	12	7	4	5	10	2	21
    cout << endl;
    tree.Traverse3();
}
/* 调用Test,运行结果为
18	12	7	4	5	10	2	21
18	12	7	4	5	10	2	21
18	12	7	4	5	10	2	21
 */
posted @ 2018-10-15 18:36  一只美丽的呱呱  阅读(502)  评论(0编辑  收藏  举报