二叉树的操作--C语言实现
树是一种比较复杂的数据结构,它的操作也比较多。常用的有二叉树的创建,遍历,线索化,线索化二叉树的遍历,这些操作又可以分为前序,中序和后序。其中,二叉树的操作有递归与迭代两种方式,鉴于我个人的习惯,在这里我是使用递归来操作的,另外,层序遍历需要借助队列来实现。代码亲测,可执行。
1 #include<stdio.h>
2 #include<malloc.h>
3 typedef int ElemType; //数据类型
4
5 typedef struct BiTreeNode //二叉树结构体
6 {
7 ElemType date; //结点数据
8 struct BiTreeNode *lChild; //左指针
9 int lFlag; //左标记(==0时,左指针存储左孩子结点;==1时,左指针存储前驱结点)
10 struct BiTreeNode *rChild; //右指针
11 int rFlag; //右标记(==0时,右指针存储右孩子结点;==1时,右指针存储后继结点)
12 }*BiTree;
13 BiTree pre;
14
15 typedef struct QNode //结点结构体
16 {
17 BiTree date; //结点数据
18 struct QNode *next; //结点指针
19 }*LinkQuePtr; //结点名
20
21 typedef struct //链队结构体
22 {
23 LinkQuePtr front; //队头结点
24 LinkQuePtr rear; //队尾结点
25 }LinkQue; //队名
26
27 LinkQuePtr head = (LinkQuePtr)malloc(sizeof(QNode)); //头结点
28
29 /*链队的入队操作*/
30 int EnQueue(LinkQue *Q, BiTree e)
31 {
32 LinkQuePtr s = (LinkQuePtr)malloc(sizeof(QNode)); //申请新结点空间
33 if(!s)
34 return 0;
35 s->date = e; //新结点的数据等于e
36 s->next = NULL; //新结点的指针指向空
37 Q->rear->next = s; //原队尾结点的指针指向新结点
38 Q->rear = s; //队尾指针指向新结点(使新结点成为队尾结点)
39 return 1;
40 }
41
42 /*链队的出队操作*/
43 int DeQueue(LinkQue *Q)
44 {
45 if(Q->front == Q->rear) //判断队列是否为空
46 return 0;
47 LinkQuePtr s = (LinkQuePtr)malloc(sizeof(QNode)); //申请结点空间s
48 s = Q->front->next; //s结点等于队头结点(头指针所指向的结点)
49 Q->front->next = s->next; //头结点的指针指向s结点的下一结点(使s结点的下一结点成为队头元素)
50 if(Q->rear == s) //判断s是否为队尾元素,若是,说明队列中仅有一个结点
51 Q->rear = Q->front; //使队尾结点指向头结点
52 free(s); //释放s结点
53 return 1;
54 }
55
56 /*创建二叉树函数*/
57 void CreatBiTree(BiTree *T)
58 {
59 ElemType e; //结点数据
60 scanf("%d", &e);
61 if(e == -1) //如果输入为-1,当前结点为空
62 (*T) = NULL;
63 else
64 {
65 (*T) = (BiTree)malloc(sizeof(BiTreeNode)); //申请结点空间
66 (*T)->date = e; //为当前结点赋值
67 printf("请输入当前结点 %d 的左孩子,若没有左孩子,请输入-1\n", e);
68 CreatBiTree(&((*T)->lChild)); //递归创建左子树
69 printf("请输入当前结点 %d 的右孩子,若没有右孩子,请输入-1\n", e);
70 CreatBiTree(&((*T)->rChild)); //递归创建右子树
71 }
72 }
73
74 /*先序遍历二叉树*/
75 void PreorderTraversal(BiTree T)
76 {
77 if(T == NULL) //判空
78 return;
79 printf("%d ", T->date); //打印当前结点
80 PreorderTraversal(T->lChild); //递归遍历左子树
81 PreorderTraversal(T->rChild); //递归遍历右子树
82 }
83
84 /*中序遍历二叉树*/
85 void InorderTraversal(BiTree T)
86 {
87 if(T == NULL) //判空
88 return;
89 InorderTraversal(T->lChild); //递归左子树
90 printf("%d ", T->date); //打印当前结点
91 InorderTraversal(T->rChild); //递归右子树
92 }
93
94 /*后序遍历二叉树*/
95 void PostorderTraversal(BiTree T)
96 {
97 if(T == NULL) //判空
98 return;
99 PostorderTraversal(T->lChild); //递归左子树
100 PostorderTraversal(T->rChild); //递归右子树
101 printf("%d ", T->date); //打印当前结点
102 }
103
104 /*层序遍历二叉树*/
105 void LevelTraversal(BiTree T)
106 {
107 if(T == NULL) //判空
108 return;
109 LinkQue Q; //创建队Q
110 Q.front = head; //初始化队列
111 Q.rear = head;
112 EnQueue(&Q, T); //将根结点入队
113 while(Q.front != Q.rear) //判断队列是否为空
114 {
115 BiTree s = Q.front->next->date; //获得队列中第一个结点的数据
116 printf("%d ", s->date); //打印当前结点的数据
117 if(s->lChild) //若该结点有左孩子,将其左孩子入队
118 EnQueue(&Q, s->lChild);
119 if(s->rChild) //若该结点有右孩子,将其右孩子入队
120 EnQueue(&Q, s->rChild);
121 DeQueue(&Q); //将队列中第一个结点出队
122 }
123 }
124
125 /*计算树的深度*/
126 int Depth(BiTree T)
127 {
128 if(T == NULL) //如果当前结点为空,返回0
129 return 0;
130 int L = Depth(T->lChild); //遍历左子树
131 int R = Depth(T->rChild); //遍历右子树
132 if(L > R) //取最大值返回
133 return (L+1);
134 else
135 return (R+1);
136 }
137
138 /*中序遍历线索化*/
139 void Inorder_Traversal_Cue(BiTree &T)
140 {
141 if(T)
142 {
143 Inorder_Traversal_Cue(T->lChild); //递归左子树
144 if(T->lChild == NULL) //左孩子为空
145 {
146 T->lFlag = 1; //左标记为1
147 T->lChild = pre; //左指针指向前一结点
148 }
149 else
150 {
151 T->lFlag = 0;
152 }
153 if(pre->rChild == NULL) //前一结点的右孩子为空
154 {
155 pre->rFlag = 1; //前一结点的右标记为1
156 pre->rChild = T; //前一结点的右指针指向当前结点
157 }
158 else
159 {
160 T->rFlag = 0;
161 }
162 pre = T; //使当前结点成为前一结点
163 Inorder_Traversal_Cue(T->rChild); //递归右子树
164 }
165 }
166
167 /*添加头结点,将二叉树线索化*/
168 BiTree AddHead(BiTree &T)
169 {
170 BiTree head = (BiTree)malloc(sizeof(BiTreeNode)); //申请头结点
171 head->lFlag = 0; //头结点左标记为0
172 head->rFlag = 1; //右标记为1
173 if(!T) //若二叉树为空
174 {
175 head->lChild = head; //左指针回指
176 head->rChild = head; //右指针回指
177 return NULL;
178 }
179 pre = head; //前一结点指向头结点
180 head->lChild = T; //头结点的左孩子指向根结点
181 Inorder_Traversal_Cue(T); //中序线索化
182 pre->rChild = head; //为最后一个结点设置右指针指向头结点
183 pre->rFlag = 1; //右标记为1
184 head->rChild = pre; //头结点的右指针指向尾结点
185 return head; //返回头结点
186 }
187
188 /*遍历线索二叉树*/
189 void TreeCueTraversal(BiTree T)
190 {
191 BiTree p = T->lChild; //申请结点p指向根结点
192 while(p != T) //根结点不为空
193 {
194 while(p->lFlag == 0) //一直寻找第一个左标记为1的结点
195 p = p->lChild;
196 printf("%d ", p->date); //打印第一个结点
197 while(p->rFlag == 1 && p->rChild != T) //若右标记是1,且右孩子不是头结点
198 {
199 p = p->rChild; //一直遍历
200 printf("%d ", p->date);
201 }
202 p = p->rChild; //若右标记为0,p赋值为p的右子树
203 }
204 printf("\n");
205 }
206
207 void main()
208 {
209 BiTree T; //声明一个树变量
210 int dep = 0; //树深度变量
211
212 while(true)
213 {
214 printf("请选择对二叉树的操作:\n");
215 printf("1.创建\n");
216 printf("2.先序遍历\n");
217 printf("3.中序遍历\n");
218 printf("4.后序遍历\n");
219 printf("5.层序遍历\n");
220 printf("6.获取深度\n");
221 printf("7.中序线索化\n");
222 printf("8.遍历线索化二叉树\n");
223 printf("9.退出\n");
224 int a;
225 scanf("%d", &a);
226 switch(a)
227 {
228 case 1:
229 printf("请输入根节点:\n");
230 CreatBiTree(&T);
231 break;
232 case 2:
233 PreorderTraversal(T);
234 break;
235 case 3:
236 InorderTraversal(T);
237 break;
238 case 4:
239 PostorderTraversal(T);
240 break;
241 case 5:
242 LevelTraversal(T);
243 break;
244 case 6:
245 dep = Depth(T);
246 printf("树的深度为 %d\n", dep);
247 break;
248 case 7:
249 T = AddHead(T);
250 break;
251 case 8:
252 TreeCueTraversal(T);
253 break;
254 case 9:
255 return;
256 default:
257 printf("选择错误\n");
258 break;
259 }
260 }
261 }