二叉树的文本模式输出

转自:http://youthlin.com/2014868.html

上周一数据结构上机,题目是”二叉树相关算法的实验验证”,其中要求 为便于观察程序的运行结果,设计的输出函数能在输出设备上以图形或表格或其它直观的形式输出计算结果。

好吧,比较友好的让他显示树状就行了,可是想了很久就是不知道怎么办。。。上网搜索了一下”输出二叉树“,真正找到两篇有用的文章,一篇是CSDN copica博客的,一篇是中国知网的期刊论文(江顺亮,任燕)(我连论文都搜到了!!) 不过两篇都写的好复杂啊,copica君贴的代码太长了,看不太懂,我就把它打印下来了(这就是上一篇文章的作用…把网页选定的内容另存为pdf),顺便把知网那篇论文也下载了pdf一起打印出来。研究了一下结合两篇文章终于在上周六完成作业了哈哈~


算法思路: 首先进行非递归的中根遍历,遍历过程中设置每个结点的坐标(即距离屏幕左边的偏移量offset),然后层次遍历根据坐标确定位置,因此需要给结点的struct增加几个数据域:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//结点声明
template < typename T > class BinTreeNode {
private :
BinTreeNode < T > * left , * right ; //左右子树链接
T data ; //数据域
int x = 0 ; //输出时的相对位置,在中根遍历时设置
int offset = - 1 ; //距最左边的偏移量;即坐标。
int lw = - 1 ; //左子宽度
int w = - 1 ; //数据宽度
int rw = - 1 ; //右子宽度
public :
//构造函数
BinTreeNode ( const T & item , BinTreeNode < T > * lptr = NULL , BinTreeNode < T > * rptr = NULL ) : data ( item ) , left ( lptr ) , right ( rptr ) { }
 
BinTreeNode < T > * GetLeft ( void ) const { return left ; }    //返回左结点
void SetLeft ( BinTreeNode < T > * l ) { left = l ; }    //设置左结点
BinTreeNode < T > * GetRight ( void ) const { return right ; }    //返回右结点
void SetRight ( BinTreeNode < T > * r ) { right = r ; }        //设置右结点
T & GetData ( ) { return data ; }                          //返回数据域
void SetData ( const T & item ) { data = item ; }          //设置数据域
 
int & RgetX ( ) { return x ; }
int & RgetOffset ( ) { return offset ; }
 
int    SetLw ( ) ;
int    GetLw ( ) ;
int    SetW ( ) ;
int    GetW ( ) ;
int    SetRw ( ) ;
int    GetRw ( ) ;
 
//int DataWidth();
//设置数据域宽度
 
int DataWidth ( int _data ) {
int len = 0 ;
( _data < 0 ) ? len = 1 : len = 0 ; //负数多个负号
do {
len ++ ;
_data /= 10 ;
} while ( _data ) ;
return len ;
}
int DataWidth ( char _data ) { return 1 ; }
int DataWidth ( const char * _data ) { return std :: strlen ( _data ) ; }
int DataWidth ( std :: string _data ) { return _data . length ( ) ; }
} ;

然后输出时层次遍历,使用队列打印每一层,因为我采用的输出方式是copica同学的方式,因此每层分为”连接符和数据+子树连接符竖线”,但copica的代码貌似使用了另一个print类来实现,因此我根据知网那篇论文还是用队列实现,程序中结点出队后又让他入队目的是打印”子树连接符”这一层,然后出队后就是子结点入队了,这个方法真巧妙~

以下贴出完整代码~~(点击方框右上在新窗口打开)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
/*
第二次上机
 
题目:二叉树相关算法的实验验证
[实验目的]
验证二叉树的链接存储结构及其上的基本操作。
[实验内容及要求]
1、 定义链接存储的二叉树类。 √完成
2、 实验验证如下算法的正确性、各种功能及指标:
1)创建一棵二叉树,并对其初始化; √完成
2)先根、中根、后根遍历二叉树; √完成
3)在二叉树中搜索给定结点的父结点; √完成
4)搜索二叉树中符合数据域条件的结点; √完成
5)从二叉树中删除给定结点及其左右子树。 √完成
3、 为便于观察程序的运行结果,设计的输出函数能在输出设备上以图形或表格或其它直观的形式输出计算结果。 √完成!!
例如将二叉树输出为
    1
  /  
2     3
/    /
5  6 7   8
 
4、 测试程序时,对所有输入变量取遍各种有代表性的值。 √完成
5、 为了增强程序的可读性,程序中要有适当的注释。 √完成
*/
#ifndef BINTREE_
#define BINTREE_
#include "AStack.h"
#include "AQueue.h"
#include <string>
using namespace std ;
//AStack.h和AQueue.h还有本文件大部分代码都是教科书《数据结构》附录里的
 
//结点声明
template < typename T > class BinTreeNode {
private :
BinTreeNode < T > * left , * right ; //左右子树链接
T data ; //数据域
int x = 0 ; //输出时的相对位置,在中根遍历时设置
int offset = - 1 ; //距最左边的偏移量;即坐标。
int lw = - 1 ; //左子宽度
int w = - 1 ; //数据宽度
int rw = - 1 ; //右子宽度
public :
//构造函数
BinTreeNode ( const T & item , BinTreeNode < T > * lptr = NULL , BinTreeNode < T > * rptr = NULL ) : data ( item ) , left ( lptr ) , right ( rptr ) { }
 
BinTreeNode < T > * GetLeft ( void ) const { return left ; }                  //返回左结点
void SetLeft ( BinTreeNode < T > * l ) { left = l ; }                          //设置左结点
BinTreeNode < T > * GetRight ( void ) const { return right ; }                //返回右结点
void SetRight ( BinTreeNode < T > * r ) { right = r ; }                        //设置右结点
T & GetData ( ) { return data ; }                                          //返回数据域
void SetData ( const T & item ) { data = item ; }                          //设置数据域
 
int & RgetX ( ) { return x ; }
int & RgetOffset ( ) { return offset ; }
 
int    SetLw ( ) ;
int    GetLw ( ) ;
int    SetW ( ) ;
int    GetW ( ) ;
int    SetRw ( ) ;
int    GetRw ( ) ;
 
//int DataWidth();
//设置数据域宽度
 
int DataWidth ( int _data ) {
int len = 0 ;
( _data < 0 ) ? len = 1 : len = 0 ; //负数多个负号
do {
len ++ ;
_data /= 10 ;
} while ( _data ) ;
return len ;
}
int DataWidth ( char _data ) { return 1 ; }
int DataWidth ( const char * _data ) { return std :: strlen ( _data ) ; }
int DataWidth ( std :: string _data ) { return _data . length ( ) ; }
} ;
 
template < typename T > int BinTreeNode < T > :: GetLw ( ) {
if ( lw == - 1 ) lw = SetLw ( ) ;
return lw ;
}
template < typename T > int BinTreeNode < T > :: SetLw ( ) {
 
if ( left != NULL ) {
//   _a
//  |
// _b
//|
//c
//a的左边宽度=b左+b+b右。但 b+b右 < 2 时应 取2
lw = ( left -> GetW ( ) + left -> GetRw ( ) ) > 2 ? ( left -> GetW ( ) + left -> GetRw ( ) ) : 2 ;
lw += left -> GetLw ( ) ;
 
}
else lw = 0 ;
return lw ;
}
template < typename T > int BinTreeNode < T > :: GetRw ( ) {
if ( rw == - 1 ) rw = SetRw ( ) ;
return rw ;
}
template < typename T > int BinTreeNode < T > :: SetRw ( ) {
if ( right != NULL ) {
// a_
//   |
//   b
//a右宽=b左+b+b右。但 b左+b <2 时应取2
rw = ( right -> GetLw ( ) + right -> GetW ( ) ) > 2 ? ( right -> GetLw ( ) + right -> GetW ( ) ) : 2 ;
rw += right -> GetRw ( ) ;
 
}
else rw = 0 ;
return rw ;
}
template < typename T > int BinTreeNode < T > :: GetW ( ) {
if ( w == - 1 ) w = SetW ( ) ;
return w ;
}
template < typename T > int BinTreeNode < T > :: SetW ( ) {
w = DataWidth ( data ) ;
return w ;
}
 
//1、 定义链接存储的二叉树类。
//二叉树类-连接存储结构
template < typename T > class BinTree {
private :
BinTreeNode < T > * root ;    //根节点
T stop ;                  //构造二叉树时的输入结束符,即输入stop则停止输入
public :
BinTree ( BinTreeNode < T > * t = NULL ) : root ( t ) { }      //构造函数
virtual ~ BinTree ( ) { Del ( root ) ; }                  //析构函数,删除整棵二叉树,Del()函数稍后定义
 
//在以t为根的树中搜索p的父节点
BinTreeNode < T > * Father ( BinTreeNode < T > * t , BinTreeNode < T > * p ) ;
//在以t为根节点的树中查找data域值为item的结点
BinTreeNode < T > * Find ( BinTreeNode < T > * t , const T & item ) const ;
//删除t子树(包括t结点本身)
void DelSubtree ( BinTreeNode < T > * t ) ;
void Del ( BinTreeNode < T > * t ) ;
 
void PreOrder ( BinTreeNode < T > * t ) const ;    //先根遍历 并输出该树
void InOrder ( BinTreeNode < T > * t ) const ;    //中根遍历 并输出该树
void PostOrder ( BinTreeNode < T > * t ) const ;        //后根遍历 并输出该树
 
void LevelOrder ( BinTreeNode < T > * t ) const ;        //层次遍历 并输出该树
void NorecPreOrder ( BinTreeNode < T > * t ) const ;    //非递归先根遍历 并输出该树
void NorecInOrder ( BinTreeNode < T > * t , const int out = 1 ) const ;            //非递归中根遍历并计算中根序下各结点X坐标,如果out为真则输出该树
void NorecPostOrder ( BinTreeNode < T > * t ) const ;        //非递归后跟遍历 并输出该树
 
void SetX ( ) { NorecInOrder ( root , 0 ) ; }
 
void CreatBinTree ( T tostop ) ;                          //创建二叉树
BinTreeNode < T > * Creat ( ) ;
 
BinTreeNode < T > * CopyTree ( BinTreeNode < T > * t ) ;          //复制二叉树t
//输出二叉树!
void Display ( BinTreeNode < T > * t , //要输出的树
const char * msg = NULL , //msg:可选的提示信息,(应以结尾(用cout不需要也可以))
const int offset = 0 ) ; //offset:可选整体偏移量,
 
//其他操作
BinTreeNode < T > * GetRoot ( ) { return root ; }
void SetRoot ( BinTreeNode < T > * t ) { root = t ; }
T GetStop ( ) { return stop ; }
void SetStop ( T tostop ) { stop = tostop ; }
int IsEmpty ( ) { return root == NULL ? 1 : 0 ; }
} ;
 
//中根遍历-非递归
template < typename T > void BinTree < T > :: NorecInOrder ( BinTreeNode < T > * t , const int out = 1 ) const {
if ( t == NULL ) return ;
t -> SetLw ( ) ; t -> SetW ( ) ; t -> SetRw ( ) ;
AStack < BinTreeNode < T > * > s ;
int i = 0 , z = 0 ;
BinTreeNode < T > * p = t ;
while ( p -> GetLeft ( ) != NULL ) { p = p -> GetLeft ( ) ; }
while ( t != NULL || ! s . IsEmpty ( ) ) {
while ( t != NULL ) {
s . Push ( t ) ;
t = t -> GetLeft ( ) ;
}
if ( s . IsEmpty ( ) ) return ;
i ++ ;
s . Pop ( t ) ;
if ( p == t ) { t -> RgetOffset ( ) = 0 ; } //第一个结点
else {
if ( t -> GetLeft ( ) == p ) { //t是前一个节点p的父结点
/*
* _2     __1
*|      |
*1      100
*/
z = p -> GetW ( ) ;
if ( z < 2 ) z = 2 ;
t -> RgetOffset ( ) = p -> RgetOffset ( ) + z ;
//cout << p->RgetOffset() << "(p.o)" << p->GetW() << "(p.W) " << z << "n";
}
else if ( p -> GetRight ( ) == t ) { //t是前一个节点p的子节点
/*
*012   01234
*2_    200_
*  |       |
*  3       233
*/
t -> RgetOffset ( ) = p -> RgetOffset ( ) + p -> GetW ( ) + 1 ;
}
else { //t和前一个结点p不是父子关系
/*
* ____1       100___
*|                  |
*3_               __3
*  |             |
*  200           200
*/
t -> RgetOffset ( ) = p -> RgetOffset ( ) + p -> GetW ( ) ;
}
p = t ;
//cout << p->RgetOffset() << ") ";
 
} //t不是第一个结点
if ( out ) { std :: cout << t -> GetData ( ) << " " ; }
t -> RgetX ( ) = i ;
 
t = t -> GetRight ( ) ;
}
 
}
 
//层次遍历 (使用了队列AQueue类)
template < typename T > void BinTree < T > :: LevelOrder ( BinTreeNode < T > * t ) const {
 
/*
//书上的,一次全部输出
if (t == NULL)return;
BinTreeNode<T> *p;
AQueue<BinTreeNode<T>*> q;
 
q.QInsert(t);
while (!q.isEmpty()){
q.QDelete(p);
std::cout << p->GetData() << " ";
if (p->GetLeft() != NULL)q.QInsert(p->GetLeft());
if (p->GetRight() != NULL)q.QInsert(p->GetRight());
}
*/
if ( t == NULL ) return ;
BinTreeNode < T > * p = t , * end = new BinTreeNode < T > ( stop ) ;
AQueue < BinTreeNode < T > * > q ;
q . QInsert ( p ) ;
q . QInsert ( end ) ;
while ( true )
{
q . QDelete ( p ) ;
if ( q . isEmpty ( ) ) break ;
if ( p != end ) { std :: cout << p -> GetData ( ) << " " ; }
else { std :: cout << "n" ; q . QInsert ( end ) ; }
if ( p -> GetLeft ( ) != NULL ) { q . QInsert ( p -> GetLeft ( ) ) ; }
if ( p -> GetRight ( ) != NULL ) { q . QInsert ( p -> GetRight ( ) ) ; }
}
std :: cout << "n" ;
delete p ;
//delete end;
}
 
//输出二叉树!!
template < typename T > void BinTree < T > :: Display ( BinTreeNode < T > * t , const char * msg , const int offset ) {
if ( msg ) { cout << msg << "n" ; }
//cout << "DDDDDDDDDDDDDDDDDDDDDDDDDDDDn";//
if ( NULL == t ) return ;
NorecInOrder ( t , 0 ) ; //设置每个结点的宽度、坐标。
BinTreeNode < T > * p = t , * end = new BinTreeNode < T > ( stop ) ;
AQueue < BinTreeNode < T > * > q ;
q . QInsert ( p ) ; q . QInsert ( end ) ;
int j = 0 ; //光标的当前坐标
for ( int i = 0 ; i < offset ; i ++ ) { cout << " " ; }
while ( true )
{
if ( q . QFront ( ) == end ) { cout << "n" ; break ; }
//数据层
while ( true )
{
q . QDelete ( p ) ;
q . QInsert ( p ) ;
//cout << p->GetData() << "(data)n";
if ( p == end ) {
cout << "n" ;
for ( int i = 0 ; i < offset ; i ++ ) { cout << " " ; }
j = 0 ;
break ; }
if ( p -> GetLeft ( ) != NULL ) {
for ( ; j <= p -> GetLeft ( ) -> RgetOffset ( ) ; j ++ ) { cout << " " ; }
for ( ; j < p -> RgetOffset ( ) ; j ++ ) { cout << "_" ; }
 
}
else {
for ( ; j < p -> RgetOffset ( ) ; j ++ ) { cout << " " ; }
}
 
cout << p -> GetData ( ) ; j += p -> GetW ( ) ;
 
if ( p -> GetRight ( ) != NULL ) {
for ( ; j < p -> GetRight ( ) -> RgetOffset ( ) ; j ++ ) { cout << "_" ; }
 
}
 
} //数据层
 
//“|”连接层
while ( true )
{
q . QDelete ( p ) ;
if ( p == end ) {
cout << "n" ;
for ( int i = 0 ; i < offset ; i ++ ) { cout << " " ; }
j = 0 ;
q . QInsert ( end ) ;
break ;
}
 
if ( p -> GetLeft ( ) != NULL ) {
for ( ; j < p -> GetLeft ( ) -> RgetOffset ( ) ; j ++ ) { cout << " " ; }
cout << "|" ; j ++ ; //半角管道符
//cout << "│"; j+=3;//全角制表符-不好控制
q . QInsert ( p -> GetLeft ( ) ) ; }
if ( p -> GetRight ( ) != NULL ) {
for ( ; j < p -> GetRight ( ) -> RgetOffset ( ) ; j ++ ) { cout << " " ; }
cout << "|" ; j ++ ; //半角管道符
//cout << "│"; j+=3;//全角制表符-不好控制
q . QInsert ( p -> GetRight ( ) ) ;
}
} //连接层
} //层次遍历输出
} //void
 
//1)创建一棵二叉树,并对其初始化;
//调用时必须指明tostop值,然后本函数调用Creat。
template < typename T > void BinTree < T > :: CreatBinTree ( T tostop ) {
SetStop ( tostop ) ;
root = Creat ( ) ;
}
//数据在这个函数输入,以设置的stop代替空结点,先根次序输入。
template < typename T > BinTreeNode < T > * BinTree < T > :: Creat ( ) {
BinTreeNode < T > * t , * t1 , * t2 ;
T item ;
cin >> item ;
if ( item == stop ) { t = NULL ; return t ; }
else {
if ( ! ( t = new BinTreeNode < T > ( item , NULL , NULL ) ) ) return NULL ;
t1 = Creat ( ) ;
t -> SetLeft ( t1 ) ;
t2 = Creat ( ) ;
t -> SetRight ( t2 ) ;
return t ;
}
}
 
//2)先根、中根、后根遍历二叉树;
 
//先根遍历-递归
template < typename T > void BinTree < T > :: PreOrder ( BinTreeNode < T > * t ) const {
if ( t != NULL ) {
std :: cout << t -> GetData ( ) << " " ;
PreOrder ( t -> GetLeft ( ) ) ;
PreOrder ( t -> GetRight ( ) ) ;
}
}
//中根遍历-递归
template < typename T > void BinTree < T > :: InOrder ( BinTreeNode < T > * t ) const {
if ( t != NULL ) {
InOrder ( t -> GetLeft ( ) ) ;
std :: cout << t -> GetData ( ) << " " ;
InOrder ( t -> GetRight ( ) ) ;
}
}
//后根遍历-递归
template < typename T > void BinTree < T > :: PostOrder ( BinTreeNode < T > * t ) const {
if ( t != NULL ) {
PostOrder ( t -> GetLeft ( ) ) ;
PostOrder ( t -> GetRight ( ) ) ;
std :: cout << t -> GetData ( ) << " " ;
}
}
 
//3)在二叉树中搜索给定结点的父结点;
template < typename T > BinTreeNode < T > * BinTree < T > :: Father ( BinTreeNode < T > * t , BinTreeNode < T > * p ) {
BinTreeNode < T > * q ;
if ( t == NULL || p == NULL ) return NULL ;                //若其中之一为空返回空
if ( t -> GetLeft ( ) == p || t -> GetRight ( ) == p ) return t ;    //若t即为p父节点,直接返回t
//在左右子树中寻找(递归)
if ( ( q = Father ( t -> GetLeft ( ) , p ) ) != NULL ) return q ;
else return Father ( t -> GetRight ( ) , p ) ;
 
}
//4)搜索二叉树中符合数据域条件的结点;
template < typename T > BinTreeNode < T > * BinTree < T > :: Find ( BinTreeNode < T > * t , const T & item ) const {
BinTreeNode < T > * p , * q ;
if ( NULL == t ) return NULL ;
if ( t -> GetData ( ) == item ) return t ;
if ( ( p = Find ( t -> GetLeft ( ) , item ) ) != NULL ) return p ;
else return q = Find ( t -> GetRight ( ) , item ) ;
}
//5)从二叉树中删除给定结点及其左右子树。
//删除子树t
//需要用到Father(),Del(),BinTreeNode.
template < typename T > void BinTree < T > :: DelSubtree ( BinTreeNode < T > * t ) {
if ( NULL == t ) return ;
if ( t == root ) {
Del ( t ) ;
root = NULL ;
return ;
}
BinTreeNode < T > * p , * q ;
p = t ;
q = Father ( root , p ) ;
if ( q ) {
if ( q -> GetLeft ( ) == p ) q -> SetLeft ( NULL ) ;
if ( q -> GetRight ( ) == p ) q -> SetRight ( NULL ) ;
}
Del ( p ) ;
}
//删除树t
template < typename T > void BinTree < T > :: Del ( BinTreeNode < T > * t ) {
if ( t != NULL ) {
Del ( t -> GetLeft ( ) ) ;
Del ( t -> GetRight ( ) ) ;
delete t ;
}
}
#endif


参考:

1.http://blog.csdn.net/copica/article/details/39291141

2.http://www.cnki.net/kcms/detail/detail.aspx?filename=DNZS201216031&dbcode=CJFQ&dbname=CJFD2012&v=


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值