树
【1】树的基本概念
1: 树属于一种非线性数据结构
2: 树以分支关系描述数据元素之间的层次结构
3: 树(tree)是 n(n≥0) 个结点的有限集
4: 树的结构中,有且仅有一个特殊的结点,称为树的根结点(root)
5: 树中存在唯一的称为根的数据元素
6: 树中各子树是互不相交的集合
7: 树的根结点没有前驱结点,除根之外的所有结点都有且只有一个前驱结点
8: 树中所有的结点可以有0个或多个后继结点
9: 结点(node)——表示树中的数据元素
10: 结点的度(degree)——结点的子树个数
11: 树的度——树中结点的最大度值
12: 叶子(leaf)结点——度为0的结点
13: 分支结点——除叶子结点外的所有结点(度不为0)
14: 孩子(child)——结点的子树的根称为此结点的孩子
15: 双亲(parents)——此结点称为孩子的双亲
16: 兄弟(sibling)——具有同一双亲的孩子结点互称兄弟
17: 结点的层次(level)——从根结点算起,根为第一层,它的孩子为第二层等以此类推
18: 树的深度(depth)——树中结点的最大层次数
19: 有序树——树中各结点的子树看成从左到右是有次序的(即就是子树位置不可以互换)
20: 森林(forest)——m(m >= 0)棵互不相交的树的集合。
将一棵非空树的根结点删去,树就变成一个森林;反之,给一个森林增加一个统一的根结点,森林就变成一棵树。
21: 二叉树是n(n>0)个结点的有限集,它或为空树(n=0),或由一个根结点和两棵分别称为左子树和右子树的互不相交的二叉树构成
22: 树的高度:从结点x(某棵树的根结点)向下到某个叶结点最长简单路径中边的条数
备注:
对于整棵树而言,树的高度和深度是相等的。
而对于树中相同深度的每个结点来说,它们的高度却不一定相同。
【2】树的表示
【3】二叉树
(1)二叉树的定义
我们把满足以下两个条件的树形结构叫做二叉树(Binary Tree):
1. 每个结点的度都不大于2;
2. 每个结点的孩子结点次序不能任意颠倒。
由此定义可以看出:一个二叉树中的每个结点只能拥有0、1或2个孩子,而且每个孩子有左右之分。
而且我们把位于左边的孩子叫做左孩子,位于右边的孩子叫做右孩子。
(2)二叉树的五种基本形态
(3)二叉树的性质
(4)满二叉树和完全二叉树
在满二叉树中,每层结点都是满的,即每层结点都具有最大结点数的二叉树。
深度为k,结点数为n的二叉树,如果其结点1~n的位置序号分别与满二叉树的结点1~n的位置序号一一对应,则为完全二叉树。
满二叉树必为完全二叉树, 而完全二叉树不一定是满二叉树。
(5)完全二叉树的两个重要性质
(6)二叉树的存储结构
二叉树的结构是非线性的,每一结点最多可有两个后继。
二叉树的存储结构有两种: 顺序存储结构和链式存储结构。
1. 顺序存储结构
2. 链式存储结构
二叉树的链式存储结构代码实现如下:
1 // BinTree实现代码
2 #include <iostream>
3 #include <fstream>
4 #include "Queue.h"
5 #include "Stack.h"
6 using namespace std;
7
8 template<class Type>
9 Type Max(const Type & a, const Type & b)
10 {
11 return a > b ? a : b;
12 }
13
14 template<class Type>
15 class BinaryTree;
16
17 template<class Type>
18 class BinTreeNode
19 {
20 friend class BinaryTree<Type>;
21 private:
22 BinTreeNode<Type> *leftChild, *rightChild;
23 Type data;
24
25 public:
26 BinTreeNode():leftChild(NULL), rightChild(NULL)
27 {}
28 BinTreeNode(const Type &x, BinTreeNode<Type> *left = NULL, BinTreeNode<Type> *right = NULL)
29 : data(x)
30 , leftChild(left)
31 , rightChild(right)
32 {}
33 ~BinTreeNode()
34 {}
35
36 public:
37 Type & GetData()
38 {
39 return data;
40 }
41 BinTreeNode<Type> *& GetLeft()
42 {
43 return leftChild;
44 }
45 void SetLeft(BinTreeNode<Type>* & pLeft)
46 {
47 leftChild = pLeft;
48 }
49 BinTreeNode<Type> *& GetRight()
50 {
51 return rightChild;
52 }
53 void SetRight(BinTreeNode<Type>* & pRight)
54 {
55 rightChild = pRight;
56 }
57 Type const & GetData() const
58 {
59 return data;
60 }
61 BinTreeNode<Type> * const & GetLeft() const
62 {
63 return leftChild;
64 }
65 BinTreeNode<Type> * const & GetRight() const
66 {
67 return rightChild;
68 }
69 };
70
71 template<class Type>
72 class BinaryTree
73 {
74 private:
75 BinTreeNode<Type> *root;
76 Type RefValue;
77 //创建二叉树的两种方式:
78 void CreateTree(BinTreeNode<Type> *&t)
79 {
80 Type item;
81 cin >> item;
82 if (item == RefValue)
83 {
84 t = NULL;
85 }
86 else
87 {
88 t = new BinTreeNode<Type>(item);
89 CreateTree(t->leftChild);
90 CreateTree(t->rightChild);
91 }
92 }
93 void CreateTree(BinTreeNode<Type> *&t, istream &in)
94 {
95 Type item;
96 in >> item;
97 if (item == RefValue)
98 {
99 t = NULL;
100 }
101 else
102 {
103 t = new BinTreeNode<Type>(item);
104 CreateTree(t->leftChild, in);
105 CreateTree(t->rightChild, in);
106 }
107 }
108 // 先序遍历
109 void PreOrder(const BinTreeNode<Type> *t)const
110 {
111 if (t != NULL)
112 {
113 cout << t->data << " ";
114 PreOrder(t->leftChild);
115 PreOrder(t->rightChild);
116 }
117 }
118 // 中序遍历
119 void InOrder(const BinTreeNode<Type> *t) const
120 {
121 if (t != NULL)
122 {
123 InOrder(t->leftChild);
124 cout << t->data << " ";
125 InOrder(t->rightChild);
126 }
127 }
128 // 后序遍历
129 void PostOrder(const BinTreeNode<Type> *t) const
130 {
131 if (t != NULL)
132 {
133 PostOrder(t->leftChild);
134 PostOrder(t->rightChild);
135 cout << t->data << " ";
136 }
137 }
138 // 大小
139 int Size(BinTreeNode<Type> *t) const
140 {
141 return (NULL == t) ? 0 : 1 + Size(t->leftChild) + Size(t->rightChild);
142 }
143 // 深度
144 int Depth(BinTreeNode<Type> *t) const
145 {
146 return (NULL == t) ? 0 : 1 + Max(Depth(t->leftChild), Depth(t->rightChild));
147 }
148 // 摧毁二叉树
149 void DestroyTree(BinTreeNode<Type> *t)
150 {
151 if (t != NULL)
152 {
153 DestroyTree(t->leftChild);
154 DestroyTree(t->rightChild);
155 delete t;
156 }
157 }
158 // 拷贝
159 BinTreeNode<Type>* Copy(const BinTreeNode<Type> *r) const
160 {
161 if (NULL == r)
162 {
163 return NULL;
164 }
165 else
166 {
167 BinTreeNode<Type> *s = new BinTreeNode<Type>(r->data);
168 s->leftChild = Copy(r->leftChild);
169 s->rightChild = Copy(r->rightChild);
170 return s;
171 }
172 }
173 // 查找某元素
174 BinTreeNode<Type> * Find(BinTreeNode<Type> *cur, const Type &x) const
175 {
176 if (NULL == cur)
177 {
178 return NULL;
179 }
180 else
181 {
182 if (x == cur->data)
183 return cur;
184 BinTreeNode<Type> *p = NULL;
185 if ((p = Find(cur->leftChild, x)) != NULL)
186 return p;
187 else
188 return Find(cur->rightChild, x);
189 }
190 }
191 // 父亲节点
192 BinTreeNode<Type> *Parent(BinTreeNode<Type> *start,
193 const BinTreeNode<Type> *current) const
194 {
195 if (NULL == start)
196 {
197 return NULL;
198 }
199 else
200 {
201 if (start->leftChild == current || start->rightChild == current)
202 {
203 return start;
204 }
205 BinTreeNode<Type> *p = NULL;
206 if ((p = Parent(start->leftChild, current)) != NULL)
207 return p;
208 else
209 return Parent(start->rightChild, current);
210 }
211 }
212 // 判相等
213 bool Equal(const BinTreeNode<Type> *a, const BinTreeNode<Type> *b) const
214 {
215 if (NULL == a && NULL == b)
216 {
217 return true;
218 }
219 else if (NULL == a || NULL == b)
220 {
221 return false;
222 }
223 else
224 {
225 if (a->data != b->data)
226 return false;
227 else
228 return (Equal(a->leftChild, b->leftChild) &&
229 Equal(a->rightChild, b->rightChild));
230 }
231 }
232 // 利用栈实现先序遍历
233 void PreOrderByStack(BinTreeNode<Type>* p) const
234 {
235 SeqStack<const BinTreeNode<Type> *> sT;
236 const BinTreeNode<Type> *s = NULL;
237 if (p != NULL)
238 sT.Push(p);
239 while (!sT.IsEmpty())
240 {
241 sT.Pop(s);
242 cout << s->data << " ";
243 if (s->rightChild != NULL)
244 sT.Push(s->rightChild);
245 if (s->leftChild)
246 sT.Push(s->leftChild);
247 }
248 }
249 // 利用栈实现中序遍历
250 void InOrderByStack(BinTreeNode<Type>* p) const
251 {
252 SeqStack<const BinTreeNode<Type> *> sT;
253 const BinTreeNode<Type> *s = NULL;
254 if (p != NULL)
255 sT.Push(p);
256 while (!sT.IsEmpty())
257 {
258 while (sT.GetTop(s) && s != NULL)
259 {
260 sT.Push(s->leftChild);
261 }
262 sT.Pop(s); //空值退栈
263 if (!sT.IsEmpty())
264 {
265 sT.Pop(s);
266 cout << s->data << " ";
267 sT.Push(s->rightChild);
268 }
269 }
270 }
271 // 利用栈实现后序遍历(有标记位)
272 void PostOrderByStackFlag(const BinTreeNode<Type>* p) const
273 {
274 SeqStack<const BinTreeNode<Type> *> sT;
275 const BinTreeNode<Type> *s = NULL;
276 do
277 {
278 while (p != NULL)
279 {
280 sT.Push(p);
281 p = p->leftChild;
282 }
283 s = NULL;
284 while (!sT.IsEmpty())
285 {
286 sT.GetTop(p);
287 if (p->rightChild == s) //t:表示为null 或者 右子节点被访问过了。
288 {
289 cout << p->data << " ";
290 sT.Pop(p);
291 s = p; //记录下刚刚访问的节点
292 }
293 else
294 {
295 p = p->rightChild;
296 break;
297 }
298 }
299 } while (!sT.IsEmpty());
300 }
301 // 利用栈实现后序遍历(无标记位)
302 void PostOrderByStack(const BinTreeNode<Type>* p) const
303 {
304 SeqStack<const BinTreeNode<Type> *> sT;
305 if (NULL == p)
306 return;
307 while (1)
308 {
309 while (p)
310 {
311 sT.Push(p);
312 p = p->leftChild;
313 }
314 sT.GetTop(p);
315 while (NULL == p)
316 {
317 sT.Pop(p);
318 sT.Pop(p);
319 cout << p->data << " ";
320 if (sT.IsEmpty())
321 return;
322 sT.GetTop(p);
323 }
324 sT.Push(NULL);
325 p = p->rightChild;
326 }
327 }
328 // 利用队列实现层次遍历
329 void LevelOrder(BinTreeNode<Type> * p)
330 {
331 SeqQueue<const BinTreeNode<Type> *> sQ;
332 const BinTreeNode<Type> *s = NULL;
333 if (p != NULL)
334 sQ.EnQueue(p);
335 while (!sQ.IsEmpty())
336 {
337 sQ.DeQueue(s);
338 cout << s->data << " ";
339 if (s->leftChild != NULL)
340 {
341 sQ.EnQueue(s->leftChild);
342 }
343 if (s->rightChild != NULL)
344 {
345 sQ.EnQueue(s->rightChild);
346 }
347 }
348 cout << endl;
349 }
350 public:
351 BinaryTree():root(NULL)
352 {}
353 BinaryTree(Type x):RefValue(x), root(NULL)
354 {}
355 BinaryTree(BinaryTree<Type> &bt):RefValue(bt.RefValue)
356 {
357 root = bt.Copy();
358 }
359
360 BinaryTree<Type> & operator=(const BinaryTree<Type> &bt)
361 {
362 if (this != &bt)
363 {
364 DestroyTree(root);
365 root = bt.Copy(bt.root);
366 }
367 return *this;
368 }
369
370 ~BinaryTree()
371 {
372 DestroyTree(root);
373 root = NULL;
374 }
375 public:
376 BinTreeNode<Type> * GetRoot()
377 {
378 return root;
379 }
380
381 BinTreeNode<Type> * Copy()
382 {
383 return Copy(root);
384 }
385
386 void CreateTree()
387 {
388 CreateTree(root);
389 }
390
391 void CreateTree(istream &in)
392 {
393 CreateTree(root, in);
394 }
395
396 void PreOrder() const
397 {
398 PreOrder(root);
399 cout << endl;
400 }
401
402 void InOrder() const
403 {
404 InOrder(root);
405 cout << endl;
406 }
407
408 void PostOrder() const
409 {
410 PostOrder(root);
411 cout << endl;
412 }
413
414 void PreStack() const
415 {
416 PreOrderByStack(root);
417 cout << endl;
418 }
419
420 void InStack() const
421 {
422 InOrderByStack(root);
423 cout << endl;
424 }
425
426 void PostStack() const
427 {
428 PostOrderByStack(root);
429 cout << endl;
430 }
431
432 void PostStackFlag() const
433 {
434 PostOrderByStackFlag(root);
435 cout << endl;
436 }
437
438 void LevelOrder()
439 {
440 LevelOrder(root);
441 }
442 // 所有遍历方式:
443 void Traverse()
444 {
445 cout << "先序遍历:";
446 PreOrder();
447 cout << "中序遍历:";
448 InOrder();
449 cout << "后序遍历:";
450 PostOrder();
451 cout << "利用队列层次遍历:";
452 LevelOrder();
453 cout << "利用栈先序遍历:";
454 PreStack();
455 cout << "利用栈中序遍历:";
456 InStack();
457 cout << "利用栈后序遍历(有标记位):";
458 PostStackFlag();
459 cout << "利用栈后序遍历(无标记位):";
460 PostStack();
461 }
462
463 int Size() const
464 {
465 return Size(root);
466 }
467
468 int Depth() const
469 {
470 return Depth(root);
471 }
472
473 void DestroyTree()
474 {
475 DestroyTree(root);
476 root = NULL;
477 }
478 // 插入元素
479 bool Insert(const Type& item)
480 {
481 BinTreeNode<Type> *newnode = new BinTreeNode<Type>(item);
482 if (NULL == root)
483 {
484 root = newnode;
485 return true;
486 }
487
488 BinTreeNode<Type> *pItem = Find(item);
489 if (pItem != NULL)
490 {
491 cout << "The item " << item << " is exist!" <<endl;
492 return false;
493 }
494
495 BinTreeNode<Type> *pstart = root;
496 while (1)
497 {
498 if (item < pstart->data)
499 {
500 if (NULL == pstart->leftChild)
501 {
502 pstart->leftChild = newnode;
503 return true;
504 }
505 pstart = pstart->leftChild;
506 }
507 else
508 {
509 if (NULL == pstart->rightChild)
510 {
511 pstart->leftChild = newnode;
512 return true;
513 }
514 pstart = pstart->rightChild;
515 }
516 }
517 }
518
519 BinTreeNode<Type> * Parent(const BinTreeNode<Type> *current)const
520 {
521 return (NULL == root || root == current) ? NULL : Parent(root, current);
522 }
523
524 BinTreeNode<Type> *LeftChild(BinTreeNode<Type> *current)const
525 {
526 return current->leftChild;
527 }
528
529 BinTreeNode<Type> *RightChild(BinTreeNode<Type> *current) const
530 {
531 return current->rightChild;
532 }
533
534 BinTreeNode<Type> *Find(const Type &x) const
535 {
536 return NULL == root ? NULL : Find(root, x);
537 }
538
539 bool Equal(const BinaryTree<Type> & bt) const
540 {
541 return Equal(root, bt.root);
542 }
543
544 friend bool operator==(const BinaryTree<Type> &bt1, const BinaryTree<Type> &bt2);
545
546 template<class Type>
547 friend istream & operator>>(istream &in, BinaryTree<Type> &bt);
548 template<class Type>
549 friend ostream & operator<<(ostream &out, const BinaryTree<Type> &bt);
550 };
551
552 template<class Type>
553 istream & operator>>(istream &in, BinaryTree<Type> &bt)
554 {
555 bt.CreateTree(in);
556 return in;
557 }
558
559 template<class Type>
560 ostream & operator<<(ostream &out, const BinaryTree<Type> &bt)
561 {
562 bt.InOrder(); //按中序输出
563 return out;
564 }
565
566 template<class Type>
567 bool operator ==(const BinaryTree<Type> &bt1, const BinaryTree<Type> &bt2)
568 {
569 return bt1.Equal(bt2);
570 }
571 //////////////////////////////////////////////////
572 void main()
573 {
574 BinaryTree<char> mytree('#');
575 ifstream ifile("Test.txt", ios::in);
576 if (!ifile)
577 return;
578 // 创建二叉树
579 ifile >> mytree;
580 // 输出二叉树
581 cout << "重载输出操作符,输出二叉树:"<< mytree;
582
583 BinTreeNode<char> *s = mytree.Find('E');
584 cout << "查找节点E: " << s->GetData() << endl;
585 BinTreeNode<char> *p = mytree.Parent(s);
586 cout << "查找节点E的父节点:" << p->GetData() << endl;
587
588 cout << "所有遍历二叉树方式及其结果如下:" << endl;
589 mytree.Traverse();
590
591 cout << "二叉树的节点数:" << mytree.Size() << endl;
592 cout << "二叉树的深度:" << mytree.Depth() << endl;
593
594 cout << "拷贝构造函数重新构建新的二叉树youtree" << endl;
595 BinaryTree<char> youtree(mytree);
596 cout << "youtree 与 mytree 相等 ? " << youtree.Equal(mytree) << endl;
597 // 测试插入函数
598 if (mytree.Insert('I'))
599 {
600 cout << "插入成功后再遍历二叉树:" << endl;
601 mytree.Traverse();
602 }
603 }
以上代码中引用的queue.h文件和stack.h文件可以从《队列》 和 《栈》两篇随笔中拷贝即可。
(7)二叉树的遍历和创建二叉树
1. 二叉树的遍历
假使用L、D、R分别表示遍历左子树、访问根结点、遍历右子树
那么对二叉树的遍历顺序就可以有六种方式:
a. 访问根,遍历左子树,遍历右子树(记做DLR)
b. 访问根,遍历右子树,遍历左子树(记做DRL)
c. 遍历左子树,访问根,遍历右子树(记做LDR)
d. 遍历左子树,遍历右子树,访问根(记做LRD)
e. 遍历右子树,访问根,遍历左子树(记做RDL)
f. 遍历右子树,遍历左子树,访问根(记做RLD)
二叉树的遍历常用为三种:先序,中序,后序
先序遍历(DLR)即就是先根遍历
中序遍历(LDR)即就是中根遍历
后序遍历(LRD)即就是后根遍历
三种遍历示例如下:
2. 创建二叉树
a. 由先序序列创建二叉树
b. 先序和中序创建二叉树
c. 中序和后序创建二叉树
三种创建二叉树方式实现代码如下:
1 #include <stdio.h>
2 #include <malloc.h>
3
4 typedef char TElemType;
5
6 typedef struct BiTNode
7 {
8 struct BiTNode *leftChild, *rightChild;
9 TElemType data;
10 } BiTNode, *BiTree;
11
12 //初始化
13 void InitTree(BiTree &t)
14 {
15 t = NULL;
16 }
17 // 先序遍历
18 void PreOrder(BiTree &t)
19 {
20 if (t != NULL)
21 {
22 printf("%c ", t->data);
23 PreOrder(t->leftChild);
24 PreOrder(t->rightChild);
25 }
26 }
27 // 中序遍历
28 void InOrder(BiTree &t)
29 {
30 if (t != NULL)
31 {
32 InOrder(t->leftChild);
33 printf("%c ", t->data);
34 InOrder(t->rightChild);
35 }
36 }
37 // 后序遍历
38 void PostOrder(BiTree t)
39 {
40 if (t != NULL)
41 {
42 PostOrder(t->leftChild);
43 PostOrder(t->rightChild);
44 printf("%c ", t->data);
45 }
46 }
47 // 由先序和中序创建二叉树
48 void Pre_In_Create(char *pre, char *in, int len, BiTree &t)
49 {
50 if (len <= 0) return;
51 char *p = NULL, *q = NULL;
52 int i = 0;
53 t = (BiTree)malloc(sizeof(BiTNode));
54 t->data = pre[0]; // 根节点
55 t->leftChild = NULL;
56 t->rightChild = NULL;
57 while (i < len)
58 {
59 if (*(in + i) == pre[0])
60 break;
61 ++i;
62 }
63 p = pre + 1; // 左子树的先序序列
64 q = in;
65 Pre_In_Create(p, q, i, t->leftChild);
66 p = pre + i + 1; // 右子树的先序序列
67 q = in + i + 1;
68 Pre_In_Create(p, q, len-i-1, t->rightChild);
69 }
70 // 由中序后序创建二叉树
71 void In_Pos_Create(char *in, char *pos, int len, BiTree &t)
72 {
73 if (len <= 0) return;
74 char *p = NULL, *q = NULL;
75 int i = 0; // i 表示根节点在中序中的位置
76 t = (BiTree)malloc(sizeof(BiTNode));
77 t->data = pos[len-1]; // 确定根节点
78 t->leftChild = NULL;
79 t->rightChild = NULL;
80 while (i < len)
81 {
82 if (*(in + i) == pos[len-1])
83 break;
84 ++i;
85 }
86 p = in;
87 q = pos;
88 In_Pos_Create(p, q, i, t->leftChild);
89 p = in + i + 1;
90 q = pos + i;
91 In_Pos_Create(p, q, len-i-1, t->rightChild);
92 }
93 // 由先序序列创建二叉树
94 void CreatePre(BiTree &t, TElemType *&str)
95 {
96 if (*str == '#')
97 {
98 t = NULL;
99 }
100 else
101 {
102 t = (BiTNode *)malloc(sizeof(BiTNode));
103 t->data = *str;
104 CreatePre(t->leftChild, ++str);
105 CreatePre(t->rightChild, ++str);
106 }
107 }
108
109 void PrintAll(BiTree& t)
110 {
111 PreOrder(t);
112 printf("\n");
113 InOrder(t);
114 printf("\n");
115 PostOrder(t);
116 printf("\n");
117 }
118
119 void main()
120 {
121 printf("由先序序列创建二叉树mytree: \n");
122 TElemType *buff = "ABC##DE##F##G#H##";
123 BiTree mytree;
124 InitTree(mytree);
125 CreatePre(mytree, buff);
126 printf("Print mytree: \n");
127 PrintAll(mytree);
128
129 char *pbuff = "ABCDEFGH";
130 char *ibuff = "CBEDFAGH";
131 char *tbuff = "CEFDBHGA";
132 BiTree mypretree;
133 InitTree(mypretree);
134 printf("由先序和中序创建二叉树mypretree: \n");
135 Pre_In_Create(pbuff, ibuff, 8, mypretree);
136 printf("Print mypretree: \n");
137 PrintAll(mypretree);
138
139 BiTree mypostree;
140 InitTree(mypostree);
141 printf("由中序和后序创建二叉树mypostree: \n");
142 In_Pos_Create(ibuff, tbuff, 8, mypostree);
143 printf("Print mypostree: \n");
144 PrintAll(mypostree);
145 }
(8)线索二叉树
1. 为什么要线索化二叉树?
遍历二叉树除了设栈,要么就用递归进行。有没有其它更好的方式呢?
遍历二叉树实质上是对一个非线性结构进行线性化操作的过程。
那么既然是线性化的过程能否先像线性链表那样把树结构进行线性化?
当以二叉链表作为存储结构时,只能找到结点的左右孩子信息,并不能找到结点在任一序列中的前驱和后继信息。
所以为了方便于遍历,只有通过在每个结点上增加两个指针域,分别指示结点在任一次序遍历时得到的前驱和后继。
指向结点的前驱和后继的指针叫做线索。
加上线索的二叉树称为线索二叉树。
对二叉树以某种次序(先序,中序,后序)遍历使其变为线索二叉树的过程叫做线索化。
所谓穿线二叉树,即在一般二叉树的基础上,对每个结点进行考察:
若其左子树非空,则其左指针不变,仍指向左子女;
若其左子树为空,则让其左指针指向某种遍历顺序下该结点的前驱;
若其右子树非空,则其右指针不变,仍指向右子女;
若其右子树为空,则让其右指针指向某种遍历顺序下该结点的后继。
线索化二叉树其实通俗点说:就好像要从某个地方要到另一个地方去,以前咱是边走边找路。
而现在是根据以前“经验”(某种次序)在路上做好标记,以后你不管走多少次,只要按照标线走就行,不需要再每次都边走边找路了。
线索化的过程需要用到遍历算法,一旦线索建立后,在此结构上遍历就可以不用递归、也不用栈了,实现更加容易
总而言之:
线索化二叉树相当于是按照某种遍历的次序,提前把遍历的路径给做好标记,以后再遍历的时候就会方便很多,所以要线索化二叉树。
线索二叉树的图示:
切记:
有n个节点的二叉链表中必定存在n+1个空链域
在一棵有n个节点度为k的树中必有n(k-1)+1个空链域
【4】树和森林
(1)树的存储结构
1. 双亲表示法
由于树中的每个结点都有唯一的一个双亲结点,所以可用一组连续的存储空间(一维数组)存储树中的各个结点。
数组中的一个元素表示树中的一个结点,每个结点含两个域,数据域存放结点本身信息,双亲域指示本结点的双亲结点在数组中位置。
2. 孩子表示法
孩子表示法又分为 多重链表法 和 孩子链表表示法。
a. 多重链表法 指每个结点包括一个结点信息域和多个指针域,每个指针域指向该结点的一个孩子结点,通过各个指针域值反映出树中各结点之间的逻辑关系。
在这种表示法中,树中每个结点有多个指针域,从而形成了多条链表。
由于每个结点的孩子个数没有限制,各结点的度数又各异,所以操作不容易实现,可能会造成存储空间的浪费。
b. 孩子链表法与双亲表示法相反,便于查找树中某结点的孩子,由某结点的指针域便可实现,它的主体是一个与结点个数一样大小的一维数组。
数组的每一个元素由两个域组成,一个域用来存放结点信息,另一个用来存放指针,该指针指向由该结点孩子组成的单链表的首位置。
单链表的结构也由两个域组成,一个存放孩子结点在一维数组中的序号,另一个是指针域,指向下一个孩子。
孩子表示法方便了有关孩子的操作,但有时为了便于找到双亲,可以将双亲表示法和孩子表示法相结合:
即增加一个域,存储该结点双亲结点在数组中的序号。这种存储结构称为代双亲的孩子链表。
3. 左孩子右兄弟表示法
孩子兄弟表示法又称二叉树表示法,这是一种常用的存储结构。
它用二叉链表作为树的存储结构,链表中每个结点的两个指针域分别指向其第一个孩子结点和下一个兄弟结点。
(2) 树与二叉树的对应关系
(3) 森林与二叉树的对应关系
(4) 二叉树转换为树
(5) 树转换为二叉树
(6) 二叉树转换为森林
(7) 森林转换为二叉树
【5】霍夫曼树
关于霍夫曼树请参见随笔《赫夫曼树》