要求
- 数据域为字符的一棵二叉树用广义表形式输入,创建一个采用二叉链表存储的二叉树,并按广义表的形式输出这棵二叉树。
- 完成这棵二叉树的中序遍历的递归算法。
- 完成这棵二叉树的中序遍历的非递归算法。
主要写的是链式二叉树的遍历操作。
目录
//主函数
typedef struct BTree {
ElemType data;
struct BTree* left;
struct BTree* right;
}*BTree,BT;
int main(){
BTree root=NULL;
printf("广义表形式初始化树,如:a(b(d,e),c(f,g)):\n");
root = InitTree(root);
printf("一广义表形式输出:\n");
Print(root);
printf("\n");
int chose = 0;
while (1) {
printf("========================\n");
printf("1、***前序遍历***\n2、***中序遍历***\n3、***后序遍历***\n");
printf("4、非递归前序遍历\n5、非递归中序遍历\n6、非递归后序遍历\n");
printf("========================\n");
scanf("%d", &chose);
switch (chose) {
case 1:PreOrder(root);printf("\n"); break;//递归前序遍历
case 2:InOrder(root);printf("\n"); break;//递归中序遍历
case 3:PostOrder(root);printf("\n"); break;//递归后序遍历
case 4:PreOrder2(root); printf("\n"); break;//非递归前序遍历
case 5:InOrder2(root); printf("\n"); break;//非递归中序遍历
case 6:PostOrder2(root); printf("\n"); break;//非递归后序遍历
default:printf("退出\n"); return 0;
}
}
return 0;
}
//用广义表形式初始化
//广义表形式初始化
BTree InitTree(BTree root) {
BTree stack[15]; //创建一个栈
int top = -1; //栈顶指针
int k = 0; //儿子标志
ElemType ch; //接收数据
BTree new=NULL; //新节点
while ((ch = getchar()) != '\n') {
switch (ch) {
//遇见'('说明有左儿子
case '(':
k = 1;
stack[++top] = new;
break;
//遇见')'说明儿子完了,要退栈到父亲结点
case ')':
top--;
break;
//遇见','说明有右儿子
case ',':
k = 2;
break;
default:
new = (BTree)malloc(sizeof(BT)); //分配空间
if (new == NULL)
break;
new->left = new->right = NULL; //初始化
new->data = ch; //赋值
if (root == NULL) //根节点
root = new;
switch (k) {
case 1:stack[top]->left = new; break; //连接左儿子
case 2:stack[top]->right = new; break; //连接右儿子
}
}
}
return root;
}
//用队列初始化
//队列初始化树
#define MAXSIZE 15
BTree InitTree(BTree root) {
BTtree Queue[MAXSIZE+1]; //创建队,+1防止溢出
int front = 1; //队首指针,从1开始方便后面的左右儿子判断
int rear = 0; //队尾指针。从0开始方便后面计算
BTree new; //新节点
ElemType ch; //结点值
while ((ch = getchar()) != '\n') {
new = (BTree)malloc(sizeof(BT));
new->data = ch;
new->left = new->right = NULL;
Queue[++rear] = new; //入队
if (root == NULL)
root = new;
if (rear == front * 2) //判断是否是root的左儿子
Queue[front]->left = Queue[rear];
if (rear == front * 2 + 1) { //判断是否是root的右儿子
Queue[front]->right = Queue[rear];
front++; //如果是右儿子那代表着这个结点已经操作完了,就将front加1(出队)
}
}
return root;
}
//用广义表形式输出
//广义表形式输出
void Print(BTree root) {
BTree p = root;//用p代替root,改变p不影响root
if (p == NULL)
return;
printf("%c", p->data);
//有儿子就打印左括号
if (p->left||p->right)
printf("(");
Print(p->left);
//有右儿子才打印逗号
if (p->right)
printf(",");
Print(p->right);
//到这说明遍历完了右子树回到了父亲结点,打印右括号
if (p->left || p->right)
printf(")");
}
//递归的遍历
1、前序遍历
2、中序遍历
3、后序遍历
//前序遍历
void PreOrder(BTree root) {
if (root == NULL)
return;
BTree p = root;
printf("%c", p->data);
PreOrder(p->left);
PreOrder(p->right);
}
//中序遍历
void InOrder(BTree root) {
if (root == NULL)
return;
BTree p = root;
InOrder(p->left);
printf("%c", p->data);
InOrder(p->right);
}
//后序遍历
void PostOrder(BTree root) {
if (root == NULL)
return;
BTree p = root;
PostOrder(p->left);
PostOrder(p->right);
printf("%c", p->data);
}
//非递归的遍历
1、标准写法:
//非递归前序遍历
void PreOrder2(BTree root) {
if (root == NULL)
return;
BTree p = root; //代替root移动
BTree stack[15]; //创建一个栈
int top = -1; //栈顶指针
//书上写法:
while (p || top > -1) {
while (p) {
printf("%c", p->data);
stack[++top] = p;
p = p->left;
}
p = stack[top--];
p = p->right;
}
}
//非递归中序遍历
void InOrder2(BTree root) {
if (root == NULL)
return;
BTree p = root;
BTree stack[15];
int top = -1;
//书上写法:
while (p || top > -1) {
while (p) {
stack[++top] = p;
p = p->left;
}
p = stack[top--];
printf("%c", p->data);
p = p->right;
}
}
//非递归后序遍历
//1、第一次进栈是为了遍历左子树
//2、第二次进栈是为了遍历右子树
//3、左右遍历完了才能访问根节点
void PostOrder2(BTree root) {
if (root == NULL)
return;
BTree p = root;
BTree stack1[15];
int stack2[15];
int top = 0, b = 0;
do {
while (p) {
stack1[top] = p;
stack2[top++] = 0;
p = p->left;
}
if (top > 0) {
p = stack1[--top];
b = stack2[top];
if (b == 0) {
stack1[top] = p;
stack2[top++] = 1;
p = p->right;
}
else {
printf("%c", p->data);
p = NULL;
}
}
} while (top > 0);
}
2、自己写法:
//非递归前序遍历
void PreOrder2(BTree root) {
if (root == NULL)
return;
BTree p = root; //代替root移动
BTree stack[15]; //创建一个栈
int top = -1; //栈顶指针
while (p) { //p不等于NULL时
printf("%c", p->data); //输出
if (p->left||p->right) { //只要有儿子
if (p->left) { //如果有左儿子就入栈
stack[++top] = p;
p = p->left;
continue;
}
else { //如果没有左儿子就自己变成右儿子
p = p->right;
continue;
}
}
if (top == -1) //top==-1代表着全部元素退栈了,该退出循环了
return;
p = stack[top--]; //到这说明是叶子节点,所以p变成它的父亲结点
p = p->right; //然后指向右儿子
}
}
//非递归中序遍历
void InOrder2(BTree root) {
if (root == NULL)
return;
BTree p = root;
BTree stack[15];
int top = -1;
//先遍历到左边第一个结点并入栈
while (p->left) {
stack[++top] = p;
p = p->left;
}
//因为上面循环退出时,最后一个结点没有入栈
stack[++top] = p;
while (p) {
p = stack[top--]; //top--让top指向父节点
printf("%c", p->data); //输出当前结点
if (p->right) { //如果有右儿子
p = p->right; //p变成右儿子
while (p->left) { //如果还有左儿子就入栈
stack[++top] = p;
p = p->left;
}
printf("%c", p->data); //输出
}
if (top == -1) //出栈完毕
break;
}
}
总结:
-
递归难的地方在于逻辑理解
-
非递归后序遍历难写