前天去美團面試,二面基本一路順風,後來,碰到一個用非遞迴的方法求解二叉樹高度,並要求現場寫程式,一下就卡住了,非常不爽。這兩天把這個部分惡補了一下,總結了一下思路如下。
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