前言

数据结构中二叉树和线索二叉树的C++语言描述实现模板,有详细的步骤解析及使用示例。


代码仓库


bTree.cpp(二叉树)

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
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
//头文件————————————————————
#include <iostream>
#include <vector>

//命名空间
using namespace std;

//自定义数据类型————————————————————
typedef int ELEM_TYPE; //数据的(数据)类型 依据数据的实际类型定义

//结构体
//定义
//结点
typedef struct BTNode
{
ELEM_TYPE data; //数据
struct BTNode *lChild; //指向左孩子结点的指针
struct BTNode *rChild; //指向右孩子结点的指针
} BTNode;
// struct BTNode:结构体数据类型的名称
//如创建一个结点的语句:struct BTNode node;
//若结构体数据类型内,存在指向该结构体的指针数据类型,则必须完整命名结构体数据类型的名称
//如结点结构体BTNode内,存在指向该结构体的指针数据类型lChild和rChild,则命名语句为:typedef struct BTNode而不是typedef struct
// }BTNode;:typedef给结构体数据类型起的别名,可简化语句
//如创建一个结点的语句:BTNode node;
//结构体数据类型的名称和别名尽量一致,以方便记忆、使用

//函数声明————————————————————
void use_example(); //使用示例
void createBTree(struct BTNode *&root); //创建 前序法

void preOrderTraverse(struct BTNode *root); //前序遍历 递归法
void inOrderTraverse(struct BTNode *root); //中序遍历 递归法
void postOrderTraverse(struct BTNode *root); //后序遍历 递归法
void levelOrderTraverse(struct BTNode *root, int layer, vector<vector<ELEM_TYPE>> &res); //层序遍历 递归法

void preOrderTraverse2(struct BTNode *root); //前序遍历 迭代法
void inOrderTraverse2(struct BTNode *root); //中序遍历 迭代法
void postOrderTraverse2(struct BTNode *root); //后序遍历 迭代法
void levelOrderTraverse2(struct BTNode *root); //层序遍历 迭代法

int getDepth(struct BTNode *root); //获取深度/高度

//函数定义————————————————————
//使用示例
void use_example()
{
//创建
struct BTNode *root; //根结点指针

createBTree(root); //创建 前序法
//构造三层二叉树:
//第一层:1
//第二层:2,3
//第三层:-1,-1,-1,4
//输入数据:1,2,-1,-1,3,-1,4,-1,-1

preOrderTraverse(root); //前序遍历 递归法 输出:1,2,3,4
cout << endl;

inOrderTraverse(root); //中序遍历 递归法 输出:2,1,3,4
cout << endl;

postOrderTraverse(root); //后序遍历 递归法 输出:2,4,3,1
cout << endl;

vector<vector<ELEM_TYPE>> res; //层序遍历 递归法 输出:1,2,3,4
levelOrderTraverse(root, 0, res);
for (vector<ELEM_TYPE> vec : res)
{
for (ELEM_TYPE data : vec)
{
cout << data;
}
}
cout << endl;

preOrderTraverse2(root); //前序遍历 迭代法 输出:1,2,3,4
cout << endl;

inOrderTraverse2(root); //中序遍历 迭代法 输出:2,1,3,4
cout << endl;

postOrderTraverse2(root); //后序遍历 迭代法 输出:2,4,3,1
cout << endl;

levelOrderTraverse2(root); //层序遍历 迭代法 输出:1,2,3,4
cout << endl;

cout << getDepth(root) << endl; //获取深度/高度 输出:3

return;
}

//创建 前序法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模
//空间复杂度:递归调用栈的规模/树的深度/高度:
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模
//最坏:n。n为数据规模
//注意:参数使用*&,指向指针的引用数据类型
//若只使用*:指针数据类型,在函数体中并未进行解引用调用修改*root的值,使用malloc()修改的是root的值
//所以是值拷贝,函数返回后数据丢失。地址拷贝需要引用数据类型的配合
void createBTree(struct BTNode *&root) //参数:根结点指针
{
ELEM_TYPE value = 0; //值
cin >> value; //获取输入值

if (value == -1) //空树 约定值为-1时无结点
{
root = nullptr; //根结点指针指向空
}
else //非空树
{
root = (struct BTNode *)malloc(sizeof(struct BTNode)); //创建根结点,根结点指针指向根结点

root->data = value; //初始化根结点的数据域
createBTree(root->lChild); //递归构造左子树
createBTree(root->rChild); //递归构造右子树
}

return;
}

//前序遍历 递归法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模
//空间复杂度:递归调用栈的规模/树的深度/高度:
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模
//最坏:n。n为数据规模
void preOrderTraverse(struct BTNode *root) //参数:根结点指针
{
if (root == nullptr) //空树
{
}
else //非空树
{
cout << root->data; //输出数据
preOrderTraverse(root->lChild); //前序遍历左子树
preOrderTraverse(root->rChild); //前序遍历右子树
}

return;
}

//中序遍历 递归法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模
//空间复杂度:递归调用栈的规模/树的深度/高度:
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模
//最坏:n。n为数据规模
void inOrderTraverse(struct BTNode *root) //参数:根结点指针
{
if (root == nullptr) //空树
{
}
else //非空树
{
inOrderTraverse(root->lChild); //中序遍历左子树
cout << root->data; //输出数据
inOrderTraverse(root->rChild); //中序遍历右子树
}

return;
}

//后序遍历 递归法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模
//空间复杂度:递归调用栈的规模/树的深度/高度:
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模
//最坏:n。n为数据规模
void postOrderTraverse(struct BTNode *root) //参数:根结点指针
{
if (root == nullptr) //空树
{
}
else //非空树
{
postOrderTraverse(root->lChild); //后序遍历左子树
postOrderTraverse(root->rChild); //后序遍历右子树
cout << root->data; //输出数据
}

return;
}

//层序遍历 递归法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模
//空间复杂度:递归调用栈的规模/树的深度/高度:
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模
//最坏:n。n为数据规模
//空间复杂度:存储结点数据的二维向量:n。n为数据规模
void levelOrderTraverse(struct BTNode *root, int layer, vector<vector<ELEM_TYPE>> &res) //参数:根结点指针,层次,记录结点数据的二维向量
{
if (root == nullptr) //空树
{
}
else //非空树
{
//一维向量vec记录一层中,从左到右结点的数据
//二维向量res记录从上到下每一层的vec

//因为向量大小从0开始,约定:层次从0开始,根结点为第0层,根结点的孩子结点为第1层,依次类推
//每遍历到新的层次,当前层次layer等于res的大小,如:
//第一层:layer为0,res.size()为0
//第二层:layer为1,res已记录第0层的vec,res.size()为1
//每遍历到新的层次,创建vec,res记录vec

//依据layer定位res中的vec,记录结点的数据

//核心思想:
//本质:前序递归遍历,需要递归访问根结点的左子树和右子树
//访问结点顺序:中->左->右,处理结点顺序:每层的左->右
//所以需要结合vec、layer和res记录层次遍历结点的数据

if (layer == res.size())
{
vector<ELEM_TYPE> vec;
res.push_back(vec);
}

res[layer].push_back(root->data);

if (root->lChild != nullptr) //根结点的左孩子结点不为空
{
levelOrderTraverse(root->lChild, layer + 1, res);
}

if (root->rChild != nullptr) //根结点的右孩子结点不为空
{
levelOrderTraverse(root->rChild, layer + 1, res);
}
}

return;
}

//前序遍历 迭代法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模/用户栈规模
void preOrderTraverse2(struct BTNode *root) //参数:根结点指针
{
constexpr int maxSize = 10; //指向结点的指针的向量/栈的初始化大小 大小>=结点数
vector<struct BTNode *> stack(maxSize); //指向结点的指针的向量/栈
int top = -1; //指向栈顶结点的指针

struct BTNode *visitNode = nullptr; //指向访问结点的指针

if (root == nullptr) //空树
{
}
else //非空树
{
++top;
stack[top] = root; //根结点入栈

while (top != -1) //栈不为空时,循环
{
visitNode = stack[top]; //指向访问结点的指针指向栈顶结点
--top; //栈顶结点出栈

cout << visitNode->data; //访问结点的数据

if (visitNode->rChild != nullptr) //右孩子结点不为空
{
++top;
stack[top] = visitNode->rChild; //右孩子结点入栈
}

if (visitNode->lChild != nullptr) //左孩子结点不为空
{
++top;
stack[top] = visitNode->lChild; //左孩子结点入栈
}
}
}

return;
}

//中序遍历 迭代法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模/用户栈规模
void inOrderTraverse2(struct BTNode *root) //参数:根结点指针
{
constexpr int maxSize = 10; //指向结点的指针的向量/栈的初始化大小 大小>=结点数
vector<struct BTNode *> stack(maxSize); //指向结点的指针的向量/栈
int top = -1; //指向栈顶结点的指针

struct BTNode *visitNode = root; //指向访问结点的指针 指向根结点

if (root == nullptr) //空树
{
}
else //非空树
{
//注意:栈顶结点/根结点出栈并访问后,还没有访问右子树
//可能存在栈空,右子树不为空的情况,需要继续遍历/循环
//指向访问结点的指针指向栈顶结点/根结点的右孩子结点,若指针不空,则存在右子树,需要继续遍历/循环
while (top != -1 || visitNode != nullptr) //栈不为空或指向访问结点的指针不为空时,循环
{
//左孩子结点循环入栈
while (visitNode != nullptr) //指向访问结点的指针不为空
{
++top;
stack[top] = visitNode; //指向访问结点的指针入栈

visitNode = visitNode->lChild; //指向访问结点的指针指向访问结点的左孩子结点
}

if (top != -1) //栈不为空
{
visitNode = stack[top]; //指向访问结点的指针指向栈顶结点
--top; //栈顶结点出栈

cout << visitNode->data; //访问结点的数据

visitNode = visitNode->rChild; //指向访问结点的指针指向栈顶结点/根结点的右孩子结点
}
}
}

return;
}

//后序遍历 迭代法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模/用户栈规模
void postOrderTraverse2(struct BTNode *root) //参数:根结点指针
{
//核心思想:
//反前序遍历:中->右->左
//逆后序遍历 = 反前序遍历
//后序遍历 = 逆逆后序遍历->反前序遍历中,访问结点出栈1、入栈2再出栈2的顺序

constexpr int maxSize = 10; //指向结点的指针的向量/栈的初始化大小 大小>=结点数
vector<struct BTNode *> stack1(maxSize); //指向结点的指针的向量/栈1 反前序遍历使用
vector<struct BTNode *> stack2(maxSize); //指向结点的指针的向量/栈2 逆逆后序遍历使用
int top1 = -1; //指向栈1顶结点的指针1
int top2 = -1; //指向栈2顶结点的指针2

struct BTNode *visitNode = nullptr; //指向访问结点的指针

if (root == nullptr) //空树
{
}
else //非空树
{
//反前序遍历过程
++top1;
stack1[top1] = root; //根结点入栈1

while (top1 != -1) //栈1不为空时,循环
{
visitNode = stack1[top1]; //指向访问结点的指针指向栈1顶结点
--top1; //栈1顶结点出栈1

++top2;
stack2[top2] = visitNode; //访问结点入栈2

//反前序遍历与前序遍历操作相反
if (visitNode->lChild != nullptr) //左孩子结点不为空
{
++top1;
stack1[top1] = visitNode->lChild; //左孩子结点入栈1
}

if (visitNode->rChild != nullptr) //右孩子结点不为空
{
++top1;
stack1[top1] = visitNode->rChild; //右孩子结点入栈1
}
}

//逆逆后序遍历过程
while (top2 != -1) //栈2不为空时,循环
{
visitNode = stack2[top2]; //指向访问结点的指针指向栈2顶结点
--top2; //栈2顶结点出栈2

cout << visitNode->data; //访问结点的数据
}
}

return;
}

//层序遍历 迭代法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模
//空间复杂度:指向结点的指针的向量/循环队列的大小
void levelOrderTraverse2(struct BTNode *root) //参数:根结点指针
{
constexpr int maxSize = 10; //指向结点的指针的向量/循环队列的初始化大小 大小尽量>=结点数
vector<struct BTNode *> queue(maxSize); //指向结点的指针的向量/循环队列
int front = 0; //指向队列头结点的指针
int rear = 0; //指向队列尾结点的下一结点的指针

struct BTNode *visitNode = nullptr; //指向访问结点的指针

if (root == nullptr) //空树
{
}
else //非空树
{
queue[rear] = root; //根结点入队
rear = (rear + 1) % maxSize;

while (front != rear) //队列不为空,循环
{
visitNode = queue[front]; //指向访问结点的指针指向队列头结点
front = (front + 1) % maxSize; //队列头结点出队

cout << visitNode->data; //访问结点的数据

if (visitNode->lChild != nullptr) //左子树不为空
{
queue[rear] = visitNode->lChild; //左子树的根结点入队
rear = (rear + 1) % maxSize;
}

if (visitNode->rChild != nullptr) //右子树不为空
{
queue[rear] = visitNode->rChild; //右子树的根结点入队
rear = (rear + 1) % maxSize;
}
}
}

return;
}

//获取深度/高度
//本质:后序遍历
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模
//空间复杂度:递归调用栈的规模/树的深度/高度:
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模
//最坏:n。n为数据规模
int getDepth(struct BTNode *root) //参数:根结点指针
{
int lDepth = 0; //左子树的深度
int rDepth = 0; //右子树的深度
int depth = 0; //深度=左子树深度和右子树深度间的最大值+1

if (root == nullptr) //空树
{
depth = 0;
}
else //非空树
{
lDepth = getDepth(root->lChild); //后序遍历获取左子树的深度
rDepth = getDepth(root->rChild); //后序遍历获取右子树的深度
if (lDepth < rDepth)
{
depth = rDepth + 1;
}
else
{
depth = lDepth + 1;
}
}

return depth;
}

//主函数————————————————————
int main()
{
use_example(); //使用示例

return 0;
}

tBTree.cpp(线索二叉树)

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
//头文件————————————————————
#include <iostream>

//命名空间
using namespace std;

//自定义数据类型————————————————————
typedef int ELEM_TYPE; //数据的(数据)类型 依据数据的实际类型定义

//结构体
//结点
typedef struct TBTNode
{
ELEM_TYPE data; //数据

int lTag; //左线索标志 可用bool数据类型
int rTag; //右线索标志
struct TBTNode *lChild; //指向左孩子结点的指针
struct TBTNode *rChild; //指向右孩子结点的指针
//约定:
//当lTag=0时,lChild为指针,指向左孩子结点;当lTag=1时,lChild为线索,指向直接前驱结点
//当rTag=0时,rChild为指针,指向右孩子结点;当rTag=1时,rChild为线索,指向直接后继结点
} TBTNode;
// struct TBTNode:结构体数据类型的名称
//如创建一个结点的语句:struct TBTNode node;
//若结构体数据类型内,存在指向该结构体的指针数据类型,则必须完整命名结构体数据类型的名称
//如结点结构体TBTNode内,存在指向该结构体的指针数据类型lChild和rChild,则命名语句为:typedef struct TBTNode而不是typedef struct
// }TBTNode;:typedef给结构体数据类型起的别名,可简化语句
//如创建一个结点的语句:TBTNode node;
//结构体数据类型的名称和别名尽量一致,以方便记忆、使用

//函数声明————————————————————
void use_example(); //使用示例
void createTBTree(struct TBTNode *&root); //创建 前序法

void inThread(struct TBTNode *cur, struct TBTNode *&pre); //线索化 中序遍历法
void createInThread(struct TBTNode *root); //线索化创建 中序遍历法
struct TBTNode *inFirst(struct TBTNode *cur); //获取第一个结点 中序遍历法
struct TBTNode *inNext(struct TBTNode *cur); //获取后继结点 中序遍历法
void inOrderTraverse(struct TBTNode *root); //中序遍历

void preThread(struct TBTNode *cur, struct TBTNode *&pre); //线索化 前序遍历法
void preOrderTraverse(struct TBTNode *root); //前序遍历

void postThread(struct TBTNode *cur, struct TBTNode *&pre); //线索化 后序遍历法
//后序遍历:复杂,需要知道结点双亲->使用带标志域的三叉链表

//函数定义————————————————————
//使用示例
void use_example()
{
struct TBTNode *root; //根结点指针

createTBTree(root); //创建 前序法
//构造三层二叉树:
//第一层:1
//第二层:2,3
//第三层:-1,-1,-1,4
//输入数据:1,2,-1,-1,3,-1,4,-1,-1

createInThread(root); //线索化创建 中序遍历法

inOrderTraverse(root); //中序遍历 输出:2,1,3,4
cout << endl;

struct TBTNode *root1; //根结点指针1

createTBTree(root1); //创建 前序法
//构造三层二叉树:
//第一层:1
//第二层:2,3
//第三层:-1,-1,-1,4
//输入数据:1,2,-1,-1,3,-1,4,-1,-1

struct TBTNode *pre = nullptr; //前驱结点指针 指向空
preThread(root1, pre); //线索化 前序遍历法

preOrderTraverse(root1); //前序遍历 输入:1,2,3,4
cout << endl;

struct TBTNode *root2; //根结点指针2

createTBTree(root2); //创建 前序法
//构造三层二叉树:
//第一层:1
//第二层:2,3
//第三层:-1,-1,-1,4
//输入数据:1,2,-1,-1,3,-1,4,-1,-1

struct TBTNode *pre1 = nullptr; //前驱结点指针1 指向空
postThread(root2, pre1); //线索化 后序遍历法

return;
}

//创建 前序法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模
//空间复杂度:递归调用栈的规模/树的深度/高度:
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模
//最坏:n。n为数据规模
//注意:参数使用*&,指向指针的引用数据类型
//若只使用*:指针数据类型,在函数体中并未进行解引用调用修改*root的值,使用malloc()修改的是root的值
//所以是值拷贝,函数返回后数据丢失。地址拷贝需要引用数据类型的配合
void createTBTree(struct TBTNode *&root) //参数:根结点指针
{
ELEM_TYPE value = 0; //值
cin >> value; //获取输入值

if (value == -1) //空树 约定值为-1时无结点
{
root = nullptr; //根结点指针指向空
}
else //非空树
{
root = (struct TBTNode *)malloc(sizeof(struct TBTNode)); //创建根结点,根结点指针指向根结点

root->data = value; //初始化根结点的数据域
root->lTag = 0; //初始化根结点的左线索标志
root->rTag = 0; //初始化右结点的左线索标志
createTBTree(root->lChild); //递归构造左子树
createTBTree(root->rChild); //递归构造右子树
}

return;
}

//线索化 中序遍历法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模
//空间复杂度:递归调用栈的规模/树的深度/高度:
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模
//最坏:n。n为数据规模
void inThread(struct TBTNode *cur, struct TBTNode *&pre) //参数:当前结点指针,前驱结点指针
{
if (cur == nullptr) //空树
{
}
else //非空树
{
inThread(cur->lChild, pre); //当前结点的左子树线索化

//线索化
if (cur->lChild == nullptr) //当前结点的左孩子结点为空 建立当前结点的前驱线索
{
cur->lTag = 1; //当前结点的左线索标志置1
cur->lChild = pre; //当前结点的左孩子结点指针指向前驱结点
}
if (pre != nullptr && pre->rChild == nullptr) //前驱结点不为空且前驱结点的右孩子结点为空 建立前驱结点的后继线索
{
pre->rTag = 1; //前驱结点的右线索标志置1
pre->rChild = cur; //前驱结点的右孩子结点指针指向当前结点
}

pre = cur; //更新结点指针:前驱结点指针指向当前结点

inThread(cur->rChild, pre); //当前结点的右子树线索化
}

return;
}

//线索化创建 中序遍历法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模
//空间复杂度:递归调用栈的规模/树的深度/高度:
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模
//最坏:n。n为数据规模
void createInThread(struct TBTNode *root) //参数:根结点指针
{
struct TBTNode *pre = nullptr; //前驱结点指针 指向空

if (root == nullptr) //空树
{
}
else //非空树
{
inThread(root, pre); //线索化 中序遍历法

//已更新结点指针:前驱结点指针指向最后一个结点
//处理最后一个结点:必为叶子结点,右孩子结点为空
pre->rTag = 1;
pre->rChild = nullptr;
}

return;
}

//获取第一个结点 中序遍历法
//时间复杂度:O(n)。n为数据规模
//时间复杂度:树的深度/高度:
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模
//最坏:n。n为数据规模
//空间复杂度:O(1)。未使用辅助空间
struct TBTNode *inFirst(struct TBTNode *cur) //参数:当前结点指针 返回值:以当前结点为根的树,中序遍历时的第一个结点
{
//以当前结点为根的树,中序遍历的第一个结点是最左下结点
while (cur->lTag == 0) //当前结点的左线索标志为0
{
cur = cur->lChild; //更新当前结点指针指向当前结点的左孩子结点
}

return cur;
}

//获取后继结点 中序遍历法
//时间复杂度:O(n)。n为数据规模
//时间复杂度:树的深度/高度或直接获取
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模。或O(1)
//最坏:n。n为数据规模
//空间复杂度:O(1)。未使用辅助空间
struct TBTNode *inNext(struct TBTNode *cur) //参数:当前结点指针 返回值:中序遍历时,当前结点的后继结点
{
//若当前结点的右线索标志为0,存在以当前结点的右孩子结点为根的右子树,获取第一个结点
//若当前结点的右线索标志为1,右孩子结点指针指向直接后继结点,直接获取
if (cur->rTag == 0)
{
return inFirst(cur->rChild);
}
else
{
return cur->rChild;
}
}

//中序遍历
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(1)。未使用辅助空间
void inOrderTraverse(struct TBTNode *root) //参数:根结点指针
{
for (struct TBTNode *cur = inFirst(root); cur != nullptr; cur = inNext(cur))
{
cout << cur->data; //访问结点的数据
}

return;
}

//线索化 前序遍历法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模
//空间复杂度:递归调用栈的规模/树的深度/高度:
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模
//最坏:n。n为数据规模
void preThread(struct TBTNode *cur, struct TBTNode *&pre) //参数:当前结点指针,前驱结点指针
{
if (cur == nullptr) //空树
{
}
else //非空树
{
//线索化
if (cur->lChild == nullptr) //当前结点的左孩子结点为空 建立当前结点的前驱线索
{
cur->lTag = 1; //当前结点的左线索标志置1
cur->lChild = pre; //当前结点的左孩子结点指针指向前驱结点
}
if (pre != nullptr && pre->rChild == nullptr) //前驱结点不为空且前驱结点的右孩子结点为空 建立前驱结点的后继线索
{
pre->rTag = 1; //前驱结点的右线索标志置1
pre->rChild = cur; //前驱结点的右孩子结点指针指向当前结点
}

pre = cur; //更新结点指针:前驱结点指针指向当前结点

//注意:有限制条件
if (cur->lTag == 0) //当前结点的左线索标志为0,存在左孩子结点
{
preThread(cur->lChild, pre); //当前结点的左子树线索化
}
if (cur->rTag == 0) //当前结点的左线索标志为0,存在右孩子结点
{
preThread(cur->rChild, pre); //当前结点的右子树线索化
}
}

return;
}

//前序遍历
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(1)。未使用辅助空间
void preOrderTraverse(struct TBTNode *root) //参数:根结点指针
{
struct TBTNode *cur = nullptr; //当前结点指针 指向空

if (root == nullptr) //空树
{
}
else //非空树
{
cur = root; //当前结点指针指向根结点

while (cur != nullptr) //当前结点不为空时,说明未遍历完,循环
{
while (cur->lTag == 0) //当前结点的左线索标志为0,左孩子结点不为空
{
cout << cur->data; //访问当前结点的数据
cur = cur->lChild; //当前结点指针指向当前结点的左孩子结点:中->左
}

//当前结点的左线索标志为1,左孩子结点为空
cout << cur->data; //访问当前结点的数据
cur = cur->rChild;
//若当前结点的右线索标志为0,右孩子结点不为空,当前结点指针指向当前结点的右孩子结点:中->左(空)->右
//若当前结点的右线索标志为1,右孩子结点为空,当前结点指针指向当前结点的直接后继结点
}
}

return;
}

//线索化 后序遍历法
//时间复杂度:O(n)。n为数据规模
//空间复杂度:O(n)。n为数据规模
//空间复杂度:递归调用栈的规模/树的深度/高度:
//最好:[log以2为底的n]下取整+1或[log以2为底的(n+1)]上取整。n为数据规模
//最坏:n。n为数据规模
void postThread(struct TBTNode *cur, struct TBTNode *&pre) //参数:当前结点指针,前驱结点指针
{
if (cur == nullptr) //空树
{
}
else //非空树
{

postThread(cur->lChild, pre); //当前结点的左子树线索化

postThread(cur->rChild, pre); //当前结点的右子树线索化

//线索化
if (cur->lChild == nullptr) //当前结点的左孩子结点为空 建立当前结点的前驱线索
{
cur->lTag = 1; //当前结点的左线索标志置1
cur->lChild = pre; //当前结点的左孩子结点指针指向前驱结点
}
if (pre != nullptr && pre->rChild == nullptr) //前驱结点不为空且前驱结点的右孩子结点为空 建立前驱结点的后继线索
{
pre->rTag = 1; //前驱结点的右线索标志置1
pre->rChild = cur; //前驱结点的右孩子结点指针指向当前结点
}

pre = cur; //更新结点指针:前驱结点指针指向当前结点
}

return;
}

//主函数————————————————————
int main()
{
use_example(); //使用示例

return 0;
}

总结

不同参考资料中,二叉树和线索二叉树的描述实现各不相同,但基本思想是一致的。作者使用规范的变量命名、提供详细的步骤解析及使用示例,应用C++语言将其整合成模板,以帮助理解记忆。


参考资料

  • 《2023版数据结构高分笔记》主编:率辉
  • 《2023年计算机组成原理考研复习指导》组编:王道论坛
  • 《大话数据结构》作者:程杰
  • 《代码随想录》作者:孙秀洋
  • 代码随想录 (programmercarl.com)

作者的话

  • 感谢参考资料的作者/博主
  • 作者:夜悊
  • 版权所有,转载请注明出处,谢谢~
  • 如果文章对你有帮助,请点个赞或加个粉丝吧,你的支持就是作者的动力~
  • 文章在描述时有疑惑的地方,请留言,定会一一耐心讨论、解答
  • 文章在认识上有错误的地方, 敬请批评指正
  • 望读者们都能有所收获