树是一种比较复杂的数据结构,它的操作也比较多。常用的有二叉树的创建,遍历,线索化,线索化二叉树的遍历,这些操作又可以分为前序,中序和后序。其中,二叉树的操作有递归与迭代两种方式,鉴于我个人的习惯,在这里我是使用递归来操作的,另外,层序遍历需要借助队列来实现。代码亲测,可执行。
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 }