1,概述
树:
- 树是一个n个结点的有限集,如果n=0称之为空树。
- 树的定义是递归的,树中又调用了自身。
- 树的根节点没有前驱,除了根节点,其他所有节点有且只有一个前驱
- 树中所有节点可以有0个或者多个后驱。
二叉树:
遍历方式:
前序:根->左->右
中序:左->根->右
后序:左->右->根
如图所示:

在这里需要注意的是,根是相对的,左右也是相对的,以A为根,左右为B,C。以B为根,左右为D,F。
- 前序时,开始根为A然后是A-B-D,此时根为B,所以退出D进入E。
- 中序时,开始从D开始,然后根为B,所以是D-B-E,然后退出E进入A。
- 后序时,开始从D开始,然后去E,再去根B,然后进入F-G-C最后到A。
2. 树的创建
树的创建中我们每次都会调用相同的结构体,所以我们使用递归来重复定义和写入预设代码如下:
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
| #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <time.h> #include <math.h> #include <time.h> #include <Windows.h> #include <string.h> #include <malloc.h>
typedef struct TREECREAT { char name; struct TREECREAT* LEFT; struct TREECREAT* RIGHT; }TREE;
void TREERE(TREE** t) { char name; scanf(" %c",&name); if (name=='#') { *t = NULL; return; } else { *t = (TREE*)malloc(sizeof(TREE)); (*t)->name = name; TREERE(&((*t)->LEFT)); TREERE(&((*t)->RIGHT)); } }
int main() { TREE* t ; TREERE(&t); system("pause"); return 0; }
|
再上述过程中,我们将结构重复使用,避免了每次定义的繁杂过程。我们传入二级指针来修改指向。这时我们的录入得到顺序为根->左->右,并且是层级递增。如果需要先录入右侧,只需要更改两条代码的位置即可。
3.树的遍历
3.1普通遍历
树的遍历有前序,中序,后续三种。其区别并不大==仅仅需要改变代码优先执行的顺序即可==
前序如下:
1 2 3 4 5 6 7 8 9 10 11 12
| void printtree1(TREE* p) { if (p == NULL) { return; } else { printf("%c\n", p->name); printtree1(p->LEFT); printtree1(p->RIGHT); } }
|
中序如下:
1 2 3 4 5 6 7 8 9 10 11 12
| void printtree2(TREE* p) { if (p == NULL) { return; } else { printtree2(p->LEFT); printf("%c\n", p->name); printtree2(p->RIGHT); } }
|
后序如下:
1 2 3 4 5 6 7 8 9 10 11 12
| void printtree3(TREE* p) { if (p == NULL) { return; } else { printtree3(p->LEFT); printtree3(p->RIGHT); printf("%c\n", p->name); } }
|
其本质在于printf的位置,即根的输出与左右分枝的执行顺序。
3.2 层次遍历
层次遍历相当于树的同级结构的遍历,如图:

层次遍历就相当于是A->B->C->D->E->F->G的顺序进行遍历,这里b和c是同一级,defg为同一级。这里由于我们用递归定义的树结构,这里层级遍历就需要用到队的结构去实现。
由于层次遍历,我们可以改变一下录入规则去使用它
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
| #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <time.h> #include <math.h> #include <time.h> #include <Windows.h> #include <string.h> #include <malloc.h>
typedef struct TREE { int data; struct TREE* left; struct TREE* right; }TREE;
typedef struct QUEUE { TREE* arr[10]; int front; int bottom; }QUEUE;
void initqueue(QUEUE* q) { (*q).front = (*q).bottom = 0; }
void put(QUEUE* q1, TREE* p2) { (*q1).arr[(*q1).front++] = p2; }
TREE* pop(QUEUE* q) { if ((*q).front == (*q).bottom) return NULL; return ((*q).arr[(*q).bottom++]); }
TREE* search(TREE* tr) { QUEUE* q; TREE* p; initqueue(&q); if (tr->left == NULL || tr->right == NULL) { return tr; } put(&q, tr); while (1) { p = pop(&q); if (p->left != NULL) { put(&q, p->left); } else { return p; } if (p->right != NULL) { putqueue(&q, p->right); } else { return p; } } }
TREE* inittree(TREE**p) { TREE* pp = *p; char data; scanf("%c", &data); pp = (TREE*)malloc(sizeof(TREE)); pp->left = NULL; pp->right = NULL; pp->data = data; *p = pp; return *p; }
void insert() { TREE* p, * q; char ch; q = (TREE*)malloc(sizeof(TREE)); q->left = NULL; q->right = NULL; printf("请输入节点字母:\n"); ch = getchar(); q->data = ch; p = search(&q); if (p->left) { p->left = q; } else { p->right = q; } }
int main() { TREE* p; inittree(&p); system("pause"); return 0; }
|
在上述代码中,我么能用整形数来计算和存储队列,我们每次输入树的根时都会进行判定,左右,如果左没有就填充左,然后判定右边,不同的时,每次录入都会判定左右的根是否为空,然后按层次录入。从而每次录入时进行队列的录入,于是完成了层次遍历的存储条件。
3. 资料参考
【UP从0到1带你手撕数据结构全集(C语言版)】https://www.bilibili.com/video/BV1W64y1z7jh?p=13&vd_source=602097138258a0057a732e44579de1ed
【数据结构上机-通过层次遍历演示完全二叉树的建立与遍历-C语言上机代码完整实现】https://www.bilibili.com/video/BV1PF411E7ja?vd_source=602097138258a0057a732e44579de1ed
【层次遍历C语言实现】https://www.bilibili.com/video/BV1HM4y1u7fV?vd_source=602097138258a0057a732e44579de1ed