非递归方法创建、递归遍历二叉树
程序代码:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<malloc.h> #define MAXSIZE 10000 typedef struct BTreeNode{ char data; struct BTreeNode* lChild; struct BTreeNode* rChild; }BTreeNode; typedef struct{ BTreeNode* pdata[MAXSIZE]; int top; }Stack; void InitStack(Stack* S){ S->top = 0; } void Push(Stack* S, BTreeNode* T){ S->pdata[S->top] = T; S->top++; } BTreeNode* Pop(Stack* S){ if (S->top != 0){ S->top--; return S->pdata[S->top]; } else return NULL; } int StackEmpty(Stack* S) { if (S->top == 0) return 1; else return 0; } BTreeNode* GetTop(Stack* S){ if (!StackEmpty(S)) return S->pdata[S->top-1]; return NULL; } BTreeNode* Create(){ Stack* S = (Stack*)malloc(sizeof(Stack)); InitStack(S); //创建并初始化栈 char str[MAXSIZE]; scanf("%s", str); //创建字符数组并输入前序二叉树 char* p; p = str; //创建指针指向正在操作字符 BTreeNode* newnode = (BTreeNode*)malloc(sizeof(BTreeNode)); if (newnode == NULL){exit(-1);} newnode->data = *p; newnode->lChild = NULL; newnode->rChild = NULL; p++; Push(S, newnode); //根节点入栈 BTreeNode* root = newnode; //记录根节点 int flag = 0; while (*p != '\0') { if (*p == '#' && flag == 0){ flag = 1; } else if (*p == '#' && flag == 1) { BTreeNode* t = Pop(S); if (!StackEmpty(S)) { while (GetTop(S)->rChild == t) { t = Pop(S); if (StackEmpty(S))break; } } } else { newnode = (BTreeNode*)malloc(sizeof(BTreeNode)); if (newnode == NULL) { exit(-1); } newnode->data = *p; newnode->lChild = NULL; newnode->rChild = NULL; if (flag == 0) { GetTop(S)->lChild = newnode; Push(S, newnode); } else { GetTop(S)->rChild = newnode; flag = 0; Push(S, newnode); } } p++; } return root; } void PrePrint(BTreeNode* T){ if (T!=NULL) { printf("%c->", T->data); PrePrint(T->lChild); PrePrint(T->rChild); } } void InPrint(BTreeNode* T) { if (T != NULL) { InPrint(T->lChild); printf("%c->", T->data); InPrint(T->rChild); } } void PostPrint(BTreeNode* T) { if (T != NULL) { PostPrint(T->lChild); PostPrint(T->rChild); printf("%c->", T->data); } } int main() { BTreeNode* root; printf("输入一组数据(以#分隔):\n"); root = Create(); printf("前序遍历:"); PrePrint(root); printf("\n"); printf("中序遍历:"); InPrint(root); printf("\n"); printf("后序遍历:"); PostPrint(root); return 0; } |
运行截图:
非递归方法创建、非递归遍历二叉树
程序代码:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<malloc.h> #define MAXSIZE 10000 typedef struct BTreeNode { char data; struct BTreeNode* lChild; struct BTreeNode* rChild; }BTreeNode; typedef struct { BTreeNode* pdata[MAXSIZE]; int top; }Stack; void InitStack(Stack* S) { S->top = 0; } void Push(Stack* S, BTreeNode* T) { S->pdata[S->top] = T; S->top++; } BTreeNode* Pop(Stack* S) { if (S->top != 0) { S->top--; return S->pdata[S->top]; } else return NULL; } int StackEmpty(Stack* S) { if (S->top == 0) return 1; else return 0; } BTreeNode* GetTop(Stack* S) { if (!StackEmpty(S)) return S->pdata[S->top - 1]; return NULL; } BTreeNode* Create() { Stack* S = (Stack*)malloc(sizeof(Stack)); InitStack(S); //创建并初始化栈 char str[MAXSIZE]; scanf("%s", str); //创建字符数组并输入前序二叉树 char* p; p = str; //创建指针指向正在操作字符 BTreeNode* newnode = (BTreeNode*)malloc(sizeof(BTreeNode)); if (newnode == NULL) { exit(-1); } newnode->data = *p; newnode->lChild = NULL; newnode->rChild = NULL; p++; Push(S, newnode); //根节点入栈 BTreeNode* root = newnode; //记录根节点 int flag = 0; while (*p != '\0') { if (*p == '#' && flag == 0) { flag = 1; } else if (*p == '#' && flag == 1) { BTreeNode* t = Pop(S); if (!StackEmpty(S)) { while (GetTop(S)->rChild == t) { t = Pop(S); if (StackEmpty(S))break; } } } else { newnode = (BTreeNode*)malloc(sizeof(BTreeNode)); if (newnode == NULL) { exit(-1); } newnode->data = *p; newnode->lChild = NULL; newnode->rChild = NULL; if (flag == 0) { GetTop(S)->lChild = newnode; Push(S, newnode); } else { GetTop(S)->rChild = newnode; flag = 0; Push(S, newnode); } } p++; } return root; } void PrePrint(BTreeNode* T) { Stack* S=(Stack*)malloc(sizeof(Stack)); InitStack(S); BTreeNode* p = T; while (p != NULL || !StackEmpty(S)) { if (p) { printf("%c->", p->data); Push(S, p); p = p->lChild; } else { p = Pop(S); p = p->rChild; } } } void InPrint(BTreeNode* T) { Stack* S = (Stack*)malloc(sizeof(Stack)); InitStack(S); BTreeNode* p = T; while (p != NULL || !StackEmpty(S)) { if (p) { Push(S, p); p = p->lChild; } else { p = Pop(S); printf("%c->", p->data); p = p->rChild; } } } int main() { BTreeNode* root; printf("输入一组数据(以#分隔):\n"); root = Create(); printf("前序遍历:"); PrePrint(root); printf("\n"); printf("中序遍历:"); InPrint(root); return 0; } |
运行截图:
递归方法创建、递归遍历二叉树
程序代码:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<malloc.h> typedef struct BTreeNode { char data; struct BTreeNode* lChild; struct BTreeNode* rChild; }BTreeNode; BTreeNode* Create() { BTreeNode* T; char ch; scanf("%c",&ch); if (ch == '#') T = NULL; else { T = (BTreeNode*)malloc(sizeof(BTreeNode)); T->data = ch; T->lChild = Create(); T->rChild = Create(); } return T; } void PrePrint(BTreeNode* T) { if (T != NULL) { printf("%c->", T->data); PrePrint(T->lChild); PrePrint(T->rChild); } } void InPrint(BTreeNode* T) { if (T != NULL) { InPrint(T->lChild); printf("%c->", T->data); InPrint(T->rChild); } } void PostPrint(BTreeNode* T) { if (T != NULL) { PostPrint(T->lChild); PostPrint(T->rChild); printf("%c->", T->data); } } int main() { BTreeNode* root; printf("输入一组数据(以#分隔):\n"); root = Create(); printf("前序遍历:"); PrePrint(root); printf("\n"); printf("中序遍历:"); InPrint(root); printf("\n"); printf("后序遍历:"); PostPrint(root); return 0; } |
运行截图:
实验小结
非递归创建二叉树的算法思想
首先,我们对二叉树进行扩展,然后按先序遍历的顺序输入节点的信息,然后我们设置一个标志位flag,如果flag=0,创建当前节点的左孩子节点,当flag=1时,创建当前节点的右孩子节点。具体的分为以下四种情况:
1. 当前要创建的节点值为'#',并且flag=1,说明当前节点的父节点的左右孩子已经创建完毕,应将栈顶元素出栈,如果出栈的元素与栈顶的元素的右孩子相同,说明当前节点的父节点的左右孩子也创建完毕。栈顶元素继续出栈,直到栈顶的元素的右孩子和出栈的元素不相同。
2. 当前要创建的节点值'#',并且flag=0,说明当前节点的父节点的左孩子已经创建完毕,转向创建右孩子,把flag置为1;
3. 当前要创建的节点值不等于'#',并且flag=0,创建当前节点 并作为栈顶元素的左孩子,同时把当前节点入栈。(保存节点,创建右孩子)。
4. 当前要创建的节点值不等于'#',并且flag=1,创建当前节点并作为栈顶元素的右孩子。同时把当前节点入栈。
递归的本质是栈
我们知道递归函数存在的最大问题是,当递归次数足够大时,会导致函数栈溢出而死机,函数栈的大小一般是一个固定值,对于Linux来说一般默认是8M。因此,许多人说不得用递归函数。那么问题来了,所有递归函数都能非递归化吗?答案是肯定的。
本质上讲,对于同一个问题,如果必然要用广义递归的方案来处理,那么狭义递归函数只不过是其中的一种实现方式,如果放弃狭义递归函数的话,我们不得不借助一个额外的数据结构:栈。如此看来,无论如何都要用到栈,只不过要么让编译器来维护一个栈(函数栈),要么让程序员来维护一个栈(数据栈)。两者区别如下:
函数栈 | 数据栈 | |
位置 | 进程的stack区 | 进程的heap区 |
大小限制 | 小 | 能分配到很大 |
每个栈“元素”所需空间 | 比较大,因为要存储函数上下文 | 可以设计到很小,比如只存储一个指针 |
栈开销 | 大 | 可以做到很小 |
代码简易程度 | 简洁易读 | 相对更复杂 |