数据结构---07---二叉树---20205106009---郭睿玥
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 | /*郭睿玥第七次算法实验作业*/ /*实验原理 二叉树的基本组成:根结点、左子树、右子树。若能依次遍历这三部分,就是遍历了二叉树。 遍历二叉树(Traversing Binary Tree):是指按指定的规律对二叉树中的每个结点访问一次且仅访问一次。 若以L、D、R分别表示遍历左子树、遍历根结点和遍历右子树,则有六种遍历方案:DLR、LDR、LRD、DRL、RDL、RLD。 若规定先左后右,则只有前三种情况,分别是: DLR——先(根)序遍历。 LDR——中(根)序遍历。 LRD——后(根)序遍历。 对于二叉树的遍历,分别讨论递归遍历算法和非递归遍历算法。 递归遍历算法具有非常清晰的结构,但初学者往往难以接受或怀疑,不敢使用。实际上,递归算法是由系统通过使用堆栈来实现控制的。 而非递归算法中的控制是由设计者定义和使用堆栈来实现的。 */ /*实验环境 CodeBlocks*/ /*实验目的 掌握二叉树的二叉链存储结构及表示。 掌握二叉树的三种遍历算法(递归和非递归两类)。 运用三种遍历的方法求解二叉树的有关问题。 */ /*实验内容 实现二叉树的二叉链表存储结构; 1. 创建一颗二叉树 2.递归前序遍历二叉树 3.递归中序遍历二叉树 4.递归后序遍历二叉树 5. 测试递归打印二叉树代码 6. 非-递归前序遍历二叉树 7. 非-递归实现中序遍历二叉树 8. 非 - 递归实现后序遍历【较为复杂的方法】 9. 非 - 递归实现后序遍历【简单的方法】 10. 二叉树的层次遍历 */ #include <stdio.h> #include <stdlib.h> #define MAXSIZE 100//定义二叉树最大节点数为100 // 二叉树的实现 // 定义 二叉树的 结构体 typedef struct node{ //二叉树结构体 char data; //数据域 struct node *left; //指向左子树的指针 struct node *right; //指向右子树的指针 }Node, *Tree; //类型别名 // 依据前序遍历创建二叉树 // 根左右:ABF##CD##E### Tree create_tree(){ // 创建二叉树 Node *root = NULL; //初始化根节点 char ch; //定义字符 scanf ( "%c" , &ch); // 输入 ABF##CD##E### if (ch != '#' ){ //如果该节点不是空的则执行 root = (Node*) malloc ( sizeof (Node)); //申请空间 root->data = ch; //该节点的数据为输入的字符 //前序递归创建二叉树 root->left = create_tree(); // 递归创建左子树 root->right = create_tree(); //递归创建右子树 } else { //如果输入的是#则表示该节点为空 root = NULL; //该节点为空 } return root; //返回根节点方便调用 } // 递归前序遍历二叉树 void preOrderRec(Tree root){ //前序递归调用 if (root != NULL){ //若该二叉树不是空树,则执行 printf ( " %c - " , root->data); //输出数据 preOrderRec(root->left); //递归输出左子树 preOrderRec(root->right); //递归输出右子树 } } void preOrderNRec(Tree root){ // 非-递归前序遍历二叉树 Tree stack[MAXSIZE], node; //创建一个数组这个数组在这个程序中可以近似一个栈,定义一个树节点,之后会通过这个节点的移动来按照前序来遍历二叉树 int top = 0; //栈顶指针 if (root == NULL){ //判断是否是空树 printf ( "tree is empty-- \n" ); //如果是空树则输出 return ; //函数结束,无返回值 } else { //如果不是空树则执行 top++; //栈顶指针加一 // 仿照一个栈 stack[top] = root; // 将根节点入栈 while (top > 0){ //如果没有全部出栈,则出栈 //出栈 node = stack[top--]; //则使该节点为栈顶节点 printf ( " %c - " , node->data); //输出该节点数据域 // 先把右子树放进去,栈是先进去后出,所以下面的左子树先出 if (node->right != NULL){ //判断该节点的右子树是否为空 stack[++top] = node->right; // 如果不是空节点则入栈以实现前序遍历 } if (node->left != NULL){ //判断该节点的左子树是否为空 stack[++top] = node->left; // 如果不是空节点则入栈以实现前序遍历 } } } } // 递归中序遍历二叉树 void inOrderRec(Tree root){ //中序递归调用 if (root != NULL){ //若该二叉树不是空树,则执行 inOrderRec(root->left); //递归输出左子树 printf ( " %c - " , root->data); //输出数据 inOrderRec(root->right); //递归输出右子树 } } // 非-递归实现中序遍历二叉树 void inOrderNRec(Tree root){ // 非-递归实现中序遍历二叉树,参数为根节点 Tree stack[MAXSIZE], node; //创建一个数组这个数组在这个程序中可以近似一个栈,定义一个树节点,之后会通过这个节点的移动,来按照前序来遍历二叉树 int top = 0; // // 判断树是否为空 if (root == NULL){ //判断是否是空树 printf ( "tree is empty-- \n" ); //如果是空树则输出 return ; //函数结束,无返回值 } node = root; //使node节点被赋值为根节点 while (node != NULL || top > 0){ //如果该节点不是空节点并且定义的栈不是经过一系列操作以后没有全部出栈则继续执行循环 // 将所有的左子树节点入栈 while (node != NULL){ //判断该节点是否是空节点,直到左子树是空 stack[++top] = node; //如果不是空节点则入栈 node = node->left; //移动节点到左孩子 } // 如果右节点为空的话,执行下列语句 node = stack[top--]; //使该节点为栈顶指针 printf ( " %c - " , node->data); //输出栈顶指针 // 扫描右节点 node = node->right; //指向右节点 } } // 递归后序遍历二叉树 void backOrderRec(Tree root){ //后序递归调用 if (root != NULL){ //若该二叉树不是空树,则执行 backOrderRec(root->left); //递归输出左子树 backOrderRec(root->right); //递归输出右子树 printf ( " %c - " , root->data); //输出数据 } } // 非 - 递归实现后序遍历 void backOrderNRec(Tree root){ //非 - 递归实现后序遍历 Node *p = root; // Node *stack[MAXSIZE]; //创建栈这栈用来存储节点 int num = 0; //栈顶指针 Node *have_visited = NULL; // while (NULL != p || num>0) //创建一个数组这个数组在这个程序中可以近似一个栈,定义一个树节点,之后会通过这个节点的移动,来按照前序来遍历二叉树 { while (NULL != p) //判断节点是否为空如果不是空,则继续循环,该节点的数据入栈,指针指向左孩子 { stack[num++] = p; //数据入栈 p = p->left; //移动指针 } p = stack[num - 1]; //取栈顶指针 if (NULL == p->right || have_visited == p->right) //如果该节点的左孩子不为空并且已经调用过该节点的左孩子则可以输出该节点的值 { printf ( " %c - " , p->data); //打印该节点的数据 num--; //出栈 have_visited = p; //表示p已经出栈 p = NULL; //p置空 } else //如果没有调用过该节点 { p = p->right; //移动指针到左孩子 } } } void backOrderNRecSimple(Tree root){ //非 - 递归实现后序遍历的方法二 Tree stack[MAXSIZE], node; int top = 0; //栈顶指针 int count = 0; //标志变量 char array[MAXSIZE]; // 使用一个数组来保存数据,方便最后数组的反转 if (root == NULL){ //如果是空栈 printf ( "tree is empty-- \n" ); //提示语句 return ; //返回 } else { top++; // 仿照一个栈 stack[top] = root; // 将根节点入栈 while (top > 0){ //出栈 node = stack[top--]; //赋值 array[count++] = node->data; // 将其保存在一个数组当中 // 先把右子树放进去,栈是先进去后出,所以下面的左子树先出 if (node->left != NULL){ //如果左子树不是空的 stack[++top] = node->left; // 入栈 } if (node->right != NULL){ //如果右子树不是空的 stack[++top] = node->right; // 入栈 } } } // 反转数组,输出 for ( int i = count-1; i >= 0; i--){ //数组的顺序就是后序遍历的顺序 printf ( " %c - " , array[i]); //逐个输出 } } // 层次遍历,打印出二叉树的值 /* 算法语言描述如下; 如果不是空树 则 1.根节点入队,通过 2. */ void Level_traversal(Tree root){ //层次遍历函数 if (root == NULL){ //判断是否是空树 printf ( "tree is empty-- \n" ); //是空树则显示是空的 return ; //返回主函数 } Tree stack[MAXSIZE], node; //数组stack来模拟队列,改变node来输出节点的值和入队 node = root; //首先使node被赋值为根节点 int front = 0; // 队首指针 int rear = 0; //队末指针 stack[rear++] = node; //根节点入队 while (front != rear){ //只要不是空队则使节点按层入队最后出队 node = stack[front++]; //使node为队首节点 printf ( " %c - " , node->data); //输出节点数据 // 左右子树入队列,由于入队是按层入得队所以移动队首可以按层序找到上一层的每个节点,通过节点可以找到该节点的左右孩子 if (node->left != NULL){ //看node节点的左节点是不是空的如果不是空的入队 stack[rear++] = node->left; //左孩子入队 } if (node->right != NULL){ //看node节点的右节点是不是空的如果不是空的入队 stack[rear++] = node->right; //右孩子入队 } } } int main(){ printf ( "starting ------ \n" ); //提示开始 Tree root = create_tree(); //创建树 printf ( "递归前序遍历--- \n" ); //提示调用递归前序遍历 preOrderRec(root); //递归前序遍历 printf ( "\n" ); //空行 printf ( "递归中序遍历--- \n" ); //提示调用递归中序遍历 inOrderRec(root); //递归中序遍历 printf ( "\n" ); //空行 printf ( "递归后序遍历--- \n" ); //提示调用递归后序遍历 backOrderRec(root); //递归后序遍历 printf ( "\n" ); //空行 printf ( "------------------\n" ); //提示 printf ( "非递归实现前序遍历--- \n" ); //提示调用非递归前序遍历 preOrderNRec(root); //调用非递归前序遍历 printf ( "\n" ); //空行 printf ( "非递归实现中序遍历--- \n" ); //提示调用非递归中序遍历 inOrderNRec(root); //调用非递归中序遍历 printf ( "\n" ); //空行 printf ( "非递归实现后序遍历--- \n" ); //提示调用非递归后序遍历 backOrderNRec(root); //调用非递归后序遍历 printf ( "\n" ); //空行 printf ( "非递归实现后序遍历 简单的方法 --- \n" ); //提示调用非递归后序遍历 backOrderNRecSimple(root); //调用非递归后序遍历 printf ( "\n" ); //空行 printf ( "层次遍历 --- \n" ); //提示调用层次遍历 Level_traversal(root); //调用层次遍历 printf ( "\n" ); //空行 return 0; //返回 } /* 实验结果 starting ------ ABF##CD##E### 递归前序遍历--- A - B - F - C - D - E - 递归中序遍历--- F - B - D - C - E - A - 递归后序遍历--- F - D - E - C - B - A - ------------------ 非递归实现前序遍历--- A - B - F - C - D - E - 非递归实现中序遍历--- F - B - D - C - E - A - 非递归实现后序遍历--- F - D - E - C - B - A - 非递归实现后序遍历 简单的方法 --- F - D - E - C - B - A - 层次遍历 --- A - B - F - C - D - E - Process returned 0 (0x0) execution time : 16.420 s Press any key to continue. */ |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通