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