二叉树遍历:递归方法与非递归方法——递归程序修改为非递归的方法

来源:互联网 发布:mac怎么打开多个终端 编辑:程序博客网 时间:2024/04/30 19:11

前天去美团面试,二面基本一路顺风,后来,碰到一个用非递归的方法求解二叉树高度,并要求现场写程序,一下就卡住了,非常不爽。这两天把这个部分恶补了一下,总结了一下思路如下。

1、考虑清楚解题类似于遍历中的前序 or 中序 or 后续;

2、考虑清楚在递归程序中,每次会有哪些内容需要进栈:节点本身、返回地址(用于告诉程序返回后应该从何处执行)、局部变量、返回值。

下面先上代码,并比较得出思路。


首先,我们看一下先序遍历的递归和非递归程序:

void PreTraverse(CNode *subTree, bool (*Visit)(CNode *)){if (subTree){Visit(subTree);PreTraverse(subTree->lchild, Visit);PreTraverse(subTree->rchild, Visit);}}void PreTraverseNonRecursive(CNode *subTree, bool (*Visit)(CNode *)) // 你会把这个函数按照后边的形式简化吗^^这个函数来源于网络,个人认为写的比较繁琐。你可以使用三个case简化。{std::stack<CNode*> s;CNode *t = subTree;s.push(t);while (!s.empty()){while (!s.empty()) // go left until the most left child{t = s.top(); // notice: without pop!!if (!t)break;Visit(t);s.push(t->lchild);}s.pop(); // pop null pointerif (!s.empty()) // right child{t = s.top(); s.pop();s.push(t->rchild);}}}

其次,中序遍历:

void InTraverse(CNode *subTree, bool (*Visit)(CNode *)){if (subTree){InTraverse(subTree->lchild, Visit);Visit(subTree);InTraverse(subTree->rchild, Visit);}}void InTraverseNonRecursiveVer2(CNode *subTree, bool (*Visit)(CNode *))    {        class CPostNode        {        public:            CNode* pNode;            int flag;            CPostNode(CNode *n, int i)            {                pNode = n;                flag = i;            };        };        std::stack<CPostNode> s;        CPostNode t(subTree, 1);        s.push(t);        while (!s.empty())        {            t= s.top(); s.pop();            switch(t.flag)            {            case 1:                t.flag = 2;                s.push(t);                if (t.pNode->lchild)                {                    s.push(CPostNode(t.pNode->lchild, 1));                }                break;            case 2:                Visit(t.pNode);                if (t.pNode->rchild)                {                    s.push(CPostNode(t.pNode->rchild, 1));                }                break;            case 3: // unable to run to here for in-traverse.                break;            }        }    } 


再然后,是后续遍历:


void PostTraverse(CNode *subTree, bool (*Visit)(CNode *)){if (subTree){PostTraverse(subTree->lchild, Visit);PostTraverse(subTree->rchild, Visit);Visit(subTree);}}void PostTraverseNonRecursive(CNode *subTree, bool (*Visit)(CNode *)){class CPostNode{public:CNode* pNode;int flag;CPostNode(CNode *n, int i){pNode = n;flag = i;};};std::stack<CPostNode> s;CPostNode t(subTree, 1);s.push(t);while (!s.empty()){t = s.top(); s.pop(); // notice: pop() here!!switch(t.flag){case 1:t.flag = 2;s.push(t);if (t.pNode->lchild){s.push(CPostNode(t.pNode->lchild, 1));}break;case 2:t.flag = 3;s.push(t);if (t.pNode->rchild){s.push(CPostNode(t.pNode->rchild, 1));}break;case 3:Visit(t.pNode);//s.pop();break;}}}

最后,也就是把我难倒的,非递归法求树的高度:

int GetSubTreeDepth(CNode *subTree){if (!subTree)return 0;return std::max<int>(GetSubTreeDepth(subTree->lchild), GetSubTreeDepth(subTree->rchild)) + 1;}int GetSubTreeDepthVer2(CNode *subTree){class CStackNode{public:CNode* pNode;int flag;int depth;CStackNode(CNode *n, int i, int d){pNode = n;flag = i;depth = d;};};std::stack<CStackNode> s;CStackNode t(subTree, 1, 1);s.push(t);while (!s.empty()){t = s.top(); s.pop();switch (t.flag){case 1:t.flag = 2;s.push(t);if (t.pNode->lchild){s.push(CStackNode(t.pNode->lchild, 1, 1));}break;case 2:t.flag = 3;s.push(t);if (t.pNode->rchild){s.push(CStackNode(t.pNode->rchild, 1, 1));}break;case 3:if (s.empty())return t.depth;CStackNode t3 = s.top(); s.pop();t3.depth = std::max<int>(t3.depth, t.depth + 1);s.push(t3);break;}}return t.depth;}

从上面的例子中,我们能清晰的看到解题的思路:用flag来标记我们访问该节点第几次,不同的次数意味着要做不同的事情,所以在程序中有不同的case执行着不同的代码。

另外,一个需要关注的地方是,在求树的高度时,case 3中需要首先判断s是否为空,若为空则可直接返回高度了~


所有代码为本人花了一天的时间完成,引用请注明出处,谢谢~

xjs.xjtu@gmail.com

2012-10-24