Background
背景
Trees are fundamental in many branches of computer science. Current state-of-the art parallel computers such as Thinking Machines' CM-5 are based on fat trees. Quad- and octal-trees are fundamental to many algorithms in computer graphics.
各種樹(資料結構)是許多電腦分支學科的基礎。當下最快的並行電腦——比如思維機CM-5——就是建立在一種稱之為“胖樹”(fat trees)的架構上的。而四叉樹和八叉樹又是許多電腦圖形學演算法的基礎。
This problem involves building and traversing binary trees.
本問題是關於建立和遍例二叉樹的。
The Problem
問題
Given a sequence of binary trees, you are to write a program that prints a level-order traversal of each tree. In this problem each node of a binary tree contains a positive integer and all binary trees have have fewer than 256 nodes.
給定一個二叉樹序列,你要寫一個程式將每棵樹按層序訪問並列印出來。在這個問題中,二叉樹的每個節都值都為正整數,且每棵樹的節點數都小於256。
In a level-order traversal of a tree, the data in all nodes at a given level are printed in left-to-right order and all nodes at level k are printed before all nodes at level k+1.
在層序遍例一個樹時,指定行中所有的結點應按照從左至右的順序列印,並且在第k行列印完之後才能列印第k+1行。
For example, a level order traversal of the tree
比如,按層序遍例下面的樹的結果是:
is: 5, 4, 8, 11, 13, 4, 7, 2, 1.
In this problem a binary tree is specified by a sequence of pairs (n,s) where n is the value at the node whose path from the root is given by the string s. A path is given be a sequence of L's and R's where L indicates a left branch and R indicates a right branch. In the tree diagrammed above, the node containing 13 is specified by (13,RL), and the node containing 2 is specified by (2,LLR). The root node is specified by (5,) where the empty string indicates the path from the root to itself. A binary tree is considered to be completely specified if every node on all root-to-node paths in the tree is given a value exactly once.
在本問題中,二叉樹是由一系列的二元組(n, s)給出的,其中n是節點的值,s是由根到該節點的路徑字串。路徑字串由一系列的“L”和“R”組成,L代表左子樹,R代表右子樹。在所示二叉樹中,值為13的節點由(13, RL)表示,值為2的節點由(2, LLR)表示。根節點由(5,)表示,其中空的路徑字串表示的路徑就是由根到根。當一個二叉樹中,所有從根到節點(已給出的)的路徑上的所有的節點都已給出且只給出一次,那麼這個二叉樹就認為是完整的定義。
The Input
輸入
The input is a sequence of binary trees specified as described above. Each tree in a sequence consists of several pairs (n,s) as described above separated by whitespace. The last entry in each tree is (). No whitespace appears between left and right parentheses.
輸入是一組按照上文所述給出的二叉樹定義。序列中的每棵樹都包含數個二元組(n, s),分別以空格隔開。每棵樹的最後是()。所有的左右括弧中間都不存在空格。
All nodes contain a positive integer. Every tree in the input will consist of at least one node and no more than 256 nodes. Input is terminated by end-of-file.
所有節點的值都為正整數。輸入的每棵樹都包括至少一個節點,且不會超過256個節點。輸入由EOF表示結束。
The Output
輸出
For each completely specified binary tree in the input file, the level order traversal of that tree should be printed. If a tree is not completely specified, i.e., some node in the tree is NOT given a value or a node is given a value more than once, then the string "not complete" should be printed.
對於輸入的每行完整定義的二叉樹,都要按層序遍例並列印。如果一棵樹沒有完整的定義,即一些節點沒有給出其值,或是一個節點重複給出,則應該列印出字串“not complete”。
Sample Input
輸入樣本
(11,LL) (7,LLL) (8,R)
(5,) (4,L) (13,RL) (2,LLR) (1,RRR) (4,RR) ()
(3,L) (4,R) ()
Sample Output
輸出樣本
5 4 8 11 13 4 7 2 1
not complete
Analysis
分析
這道是一個直接的二叉樹廣度遍例問題,按照題目要求來處理就可以了。要注意所給的二元組可能沒有數字,也可能沒有路徑串,這些情況都要很仔細的處理。此外,重複給出的節點以及未給出的節點都算輸入錯誤,全部要輸出not complete。在提交你的解答前請務必自己造幾個這樣的特殊情況檢驗一下,否則很可能WA。
此外,順便提一下廣度遍例的方法。需要建立一個鏈表(為加快插入和刪除操作)來儲存每一層的節點。先將根節點加入鏈表,然後開始依次(從左至右)遍例鏈表中的每一個節點。遇到一個不為空白的節點,則輸出之並將其刪除,然後將它的兩個子節點在原位置上插入,再向右繼續下一個節點。一直到該層所有節點遍例結束,此時鏈表中的節點即為下一層的所有節點,再從頭開始遍例即可。如此往複,一直到鏈表為空白,則說明整棵樹遍例結束。演算法時間複雜度為O(n)。
Solution
解答
#include <iostream>#include <list>#include <string>#include <sstream>using namespace std;//節點結構體,儲存節點的值及左右子節點struct NODE {int nVal; NODE *pL; NODE *pR;}//NullNode為空白節點標本const NullNode = {0, 0, 0};//刪除樹void DeleteTree(NODE *pPar) {if (pPar != NULL) {//深度遍例刪除左右子節點DeleteTree(pPar->pL), DeleteTree(pPar->pR);}delete pPar;}//為樹增加節點NODE* AddChild(NODE *pPar, int nVal, const char *pPath) {//如果父為空白,則建立父節點if (pPar == 0) {pPar = new NODE(NullNode);}//根據當前路徑字串做不同操作switch (*pPath) {//遇到右括弧,說明路徑結束,為當前父節點賦值case ')': pPar->nVal = (pPar->nVal == 0 && nVal != 0) ? nVal : -1; break;//繼續建立左/右子節點case 'L': pPar->pL = AddChild(pPar->pL, nVal, pPath + 1); break;case 'R': pPar->pR = AddChild(pPar->pR, nVal, pPath + 1); break;}return pPar;}//主函數int main(void) {//樹的根結點NODE *pRoot = 0;//迴圈處理輸入的每一個節點資料for (string strToken; cin >> strToken;) {//獲得節點資料字串指標const char *pStr = strToken.c_str();int nLen = strToken.length(), nVal = 0;//如果第二個字元不是右括弧,則添加節點後繼續輸入if (pStr[1] != ')') {//將字串轉為數字for (; isdigit(*++pStr); nVal = nVal * 10 + *pStr - '0');if (*pStr != ',') while(true);//在樹中添加該節點pRoot = AddChild(pRoot, nVal, ++pStr);continue;} //如果第二字元是右括弧,說明一棵樹輸入結束,進行遍例list<NODE*> Level(1, pRoot);stringstream ssResult;//依次遍例每一層。用Level儲存一層的節點,Level非空則繼續遍例for (list<NODE*>::iterator i = Level.begin(); !Level.empty();//如果一層遍例結束,則回到起點i = (i == Level.end() ? Level.begin() : i)) {//移除當前節點NODE *pTemp = *i;i = Level.erase(i);//如果該節點為空白,則直接繼續進行下一個節點if (pTemp == 0) {continue;}//否則在原位置上增加子節點//如果該節點的值小於或等於0,說明其重複定義或未定義if (pTemp->nVal <= 0) {//清空資料,返回錯誤。Level.clear();ssResult.str("");break;}//正確的結點//輸入其值,並在原位置上插入其子節點ssResult << pTemp->nVal << ' ';i = Level.insert(i, pTemp->pL), ++i;i = Level.insert(i, pTemp->pR), ++i;}//刪除原樹,避免記憶體泄露。DeleteTree(pRoot);pRoot = 0;strToken = ssResult.str();//如果結果為空白,說明所給資料有錯if (strToken.empty()) {strToken = "not complete";} //對於正確結果要去掉最後的一個空格else {strToken.erase(strToken.end() - 1);}//輸出結果cout << strToken << endl;}return 0;}