1. 程式人生 > 實用技巧 >試題 歷屆試題 橫向列印二叉樹

試題 歷屆試題 橫向列印二叉樹

資源限制 時間限制:1.0s 記憶體限制:256.0MB 問題描述

二叉樹可以用於排序。其原理很簡單:對於一個排序二叉樹新增新節點時,先與根節點比較,若小則交給左子樹繼續處理,否則交給右子樹。

當遇到空子樹時,則把該節點放入那個位置。

比如,10 8 5 7 12 4 的輸入順序,應該建成二叉樹如下圖所示,其中.表示空白。

...|-12
10-|
...|-8-|
.......|...|-7
.......|-5-|
...........|-4

本題目要求:根據已知的數字,建立排序二叉樹,並在標準輸出中橫向列印該二叉樹。

輸入格式

輸入資料為一行空格分開的N個整數。 N<100,每個數字不超過10000。

輸入資料中沒有重複的數字。

輸出格式

輸出該排序二叉樹的橫向表示。為了便於評卷程式比對空格的數目,請把空格用句點代替:

樣例輸入1 10 5 20 樣例輸出1
...|-20
10-|
...|-5 
樣例輸入2 5 10 20 8 4 7 樣例輸出2
.......|-20
..|-10-|
..|....|-8-|
..|........|-7
5-|
..|-4 

這題初看,除了知道這些數值是自頂向下遞減的,其他沒什麼頭緒,然後看了網上的一些程式碼,但是本人的程式碼閱讀能力有些差,還是實力太差了,所以愣是看不大懂,不過總算也提取了些重要的資訊.

1.宣告一個變數用於儲存根節點的值;

2.開闢兩個陣列L[]和R[]分別用於儲存每個節點的左子樹和右子樹的資訊;

3.寫一個add()函式處理新增新節點的操作.

瞭解了這些之後我就開始自己在紙上寫寫畫畫,既然知道了節點的行號,那麼也要確定其列號才行,然後發現除了根節點列號為0,每個節點的列號正好是其父節點的列號+父節點的數值長度+3(這個3即"-|-"的長度),於是我開闢了一個數組pos[]用於記錄節點的列號,陣列len[]記錄數字長度. 於是寫出如下(有bug的)程式碼:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4
#include <cstring> 5 #include <string> 6 #include <cmath> 7 #include <algorithm> 8 #define INF 0x3f3f3f3f 9 #define zero 1e-7 10 11 using namespace std; 12 typedef long long ll; 13 const ll mod=1e9+7; 14 const ll max_n=105; 15 16 int a[max_n]={0};//記錄輸入的數字 17 int L[max_n]={0};//記錄節點的左子樹 18 int R[max_n]={0};//記錄節點的右子樹 19 int pos[max_n]={0};//記錄節點的列號 20 int len[max_n]={0};//記錄數字的長度 21 22 //新增新節點 23 void add(int r, int x) { 24 if(x<r) {//若x<r則將x交給r的左子樹處理 25 if(!L[r]) {//若r的左子樹為空,則將x放入這個位置 26 L[r]=x; 27 pos[x]=pos[r]+len[r]+3;//x的列號,3是"-|-"的長度 28 } 29 else { 30 add(L[r], x); 31 } 32 } 33 else {//否則將x交給r的左子樹處理 34 if(!R[r]) { 35 R[r]=x; 36 pos[x]=pos[r]+len[r]+3; 37 } 38 else { 39 add(R[r], x); 40 } 41 } 42 } 43 44 int main() { 45 int k=0;//k記錄輸入數字的個數 46 int root;//記錄根節點的值 47 while(cin>>a[k]) { 48 int temp=a[k]; 49 while(temp) {//計算數字a[k]的長度 50 len[a[k]]++; 51 temp/=10; 52 } 53 if(!k) {//第一個輸入的數字是根節點 54 root=a[k]; 55 pos[a[k]]=0; 56 } 57 else { 58 add(root, a[k]); 59 } 60 k++; 61 } 62 sort(a, a+k, greater<int>());//按降序排列,由排序二叉樹的特性可知,自頂往下數值越小 63 for(int i=0; i<k; i++) {//列印輸出 64 for(int j=0; j<pos[a[i]]-2; j++) { 65 cout<<'.'; 66 } 67 if(a[i]!=root) cout<<"|-"; 68 cout<<a[i]; 69 if(L[a[i]] || R[a[i]]) cout<<"-|"; 70 cout<<endl; 71 } 72 return 0; 73 }

執行之後輸出是這樣的(Ctrl+Z+回車 結束輸入得結果)——

沒錯,漏了好幾個'|',因為我只在節點前面添加了'|',而忽略了每個父節點從其右子樹到左子樹的縱向之間的'|',這可如何是好,想了想,於是我開闢了一個二維陣列mp[][]用於記錄數字前面的'.','|' 和'-',並寫了兩個迴圈,一個用於填滿每行數字前面的'.','|'和'-',一個用於專門處理每個父節點從其右子樹到左子樹的縱向之間的'|',也就是64~89行的程式碼,當然列印輸出那裡也改了一下,以下是修改後的AC程式碼——

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <string>
 6 #include <cmath>
 7 #include <algorithm>
 8 #define INF 0x3f3f3f3f
 9 #define zero 1e-7
10 
11 using namespace std;
12 typedef long long ll;
13 const ll mod=1e9+7;
14 const ll max_n=105;
15 
16 char mp[max_n][max_n];//記錄數字前面的'.','|' 和'-' 
17 int a[max_n]={0};//記錄輸入的數字 
18 int L[max_n]={0};//記錄節點的左子樹 
19 int R[max_n]={0};//記錄節點的右子樹 
20 int pos[max_n]={0};//記錄節點的列號 
21 int len[max_n]={0};//記錄數字的長度 
22 
23 //新增新節點 
24 void add(int r, int x) { 
25     if(x<r) {//若x<r則將x交給r的左子樹處理 
26         if(!L[r]) {//若r的左子樹為空,則將x放入這個位置 
27             L[r]=x;
28             pos[x]=pos[r]+len[r]+3;//x的列號,3是"-|-"的長度 
29         }
30         else {
31             add(L[r], x);
32         }
33     }
34     else {//否則將x交給r的左子樹處理 
35         if(!R[r]) {
36             R[r]=x;
37             pos[x]=pos[r]+len[r]+3;
38         }
39         else {
40             add(R[r], x);
41         }
42     }
43 }
44 
45 int main() {
46     int k=0;//k記錄輸入數字的個數 
47     int root;//記錄根節點的值 
48     while(cin>>a[k]) {
49         int temp=a[k];
50         while(temp) {//計算數字a[k]的長度 
51             len[a[k]]++;
52             temp/=10;
53         }
54         if(!k) {//第一個輸入的數字是根節點 
55             root=a[k];
56             pos[a[k]]=0;
57         }
58         else {
59             add(root, a[k]);
60         }
61         k++;
62     }
63     sort(a, a+k, greater<int>());//按降序排列,由排序二叉樹的特性可知,自頂往下數值越小 
64     //這個迴圈是將每行的數字前面填滿'.','|'和'-' 
65     for(int i=0; i<k; i++) {
66         int j;
67         for(j=0; j<pos[a[i]]-2; j++) {
68             mp[i][j]='.';
69         }
70         mp[i][j++]='|';
71         mp[i][j]='-';
72     }
73     //這個迴圈是處理每個節點從其右子樹到左子樹的縱向之間的'|' 
74     for(int i=0; i<k; i++) {
75         int temp=pos[a[i]]+len[a[i]]+1;//這是節點a[k]右邊的'|'的列號 
76         if(R[a[i]]) {//若該節點有右子樹 
77             for(int j=i-1; ; j--) {//往上 
78                 if(a[j]>=R[a[i]]) break;
79                 mp[j][temp]='|';
80             }
81         }
82         if(L[a[i]]) {//若該節點有左子樹 
83             for(int j=i+1; ; j++) {//往下 
84                 if(a[j]<=L[a[i]]) break;
85                 mp[j][temp]='|';
86             }
87         }
88     }
89     for(int i=0; i<k; i++) {//列印輸出
90         for(int j=0; j<pos[a[i]]; j++)
91             cout<<mp[i][j];
92         cout<<a[i];
93         if(L[a[i]] || R[a[i]]) cout<<"-|";
94         cout<<endl;
95     }
96     return 0;
97 }