【c/c++】机试中的二叉树遍历问题

首先解决一个问题,int a[10]; change(a); 而子函数则写为void change(int a[]) 此时相当于传入的是指针,此时可以在子函数中实现对数组a[]的修改!

先序遍历,就是先遍历根结点,再遍历左子树,然后是右子树;中序遍历,就是先遍历左子树,再遍历根结点,再遍历右子树;后序遍历,就是先遍历左子树,再遍历右子树,最后遍历根结点。

可以根据先序遍历序列和中序遍历序列,建立起树的结构,从而得到后序遍历的序列。这里先说一下建立结构体的方法,在struct 前加上 typedef 这样 typedef struct node{}n; 此时n 是 struct node 的别名,在定义时可以使用 n n1 的这种方式,实际上着这种方式对c是合适的,但是对于c++来说是多此一举的,c中要struct node n而c++中不用typedef也可以直接使用node n。

首先观察一个二叉树:

它的先序遍历为12435,中序遍历为42135,后序遍历为42531,层序遍历为12345.

要注意的是Insert()函数起到了不断向树添加结点的作用,具体的要求应该按照题目可能是比较大小之类的,这里仅仅是相当于随机的向左右子树插入结点,Insert(NODE* &T, int x)中的T是引用,才能用create真正的从NULL的修改,否则因为T是一个指针,只能修改它所指向的那些值。层序遍历类似于BFS广度优先搜索,利用了一个队列,根结点就插入队尾,访问过元素后,再将其左右子树依次放入队尾,每次从队首出队一个结点进行访问。

下面的代码包含了递归和非递归形式的先序,中序,后序遍历以及非递归形式的层序遍历,完成了一棵完整的访问过程,以上面的树为例子,对于后序遍历,要注意的是每次访问的结点一定是在上一次访问的结点是它的直接右子树或者为空的情况,代码为:

#include<cstdio>
#include<queue>
#include<stack>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

struct NODE{
    NODE *lchild;
    NODE *rchild;
    int c;
}Tree[50];//Tree只在create中被用到,这样就不用new了

int loc;
char str[50];
int flag = 0;
queue<NODE*> q;//存入的是指针类型,队列保存的只是原元素的副本,无法直接修改,但当为地址可以对指向的值进行的修改

//创建一个新的结点,返回的是指针即地址,主要是定义左右子树指向的结点
NODE *create(int ch)
{
    Tree[loc].c = ch;
    Tree[loc].lchild = Tree[loc].rchild = NULL;
    return &Tree[loc++];
}

void preOrder(NODE *T)
{
    if(T == NULL)
        return;
    printf("%d ", T->c);
    preOrder(T->lchild);
    preOrder(T->rchild);
}

void inOrder(NODE *T)
{
    if(T == NULL)
        return;
    inOrder(T->lchild);
    printf("%d ", T->c);
    inOrder(T->rchild);
}

void postOrder(NODE *T)
{
    if(T == NULL)
        return;
    postOrder(T->lchild);
    postOrder(T->rchild);
    printf("%d ", T->c);
}

//层序遍历非递归形式,传入的参数是根结点指针
//层序遍历与广度优先搜索比较类似
void layerOrder(NODE *root)
{
    q.push(root);
    while(!q.empty())
    {
        NODE *T = q.front();
        q.pop();
        printf("%d ", T->c);//访问值
        if(T->lchild != NULL)
            q.push(T->lchild);
        if(T->rchild != NULL)
            q.push(T->rchild);
    }
}


//非递归先序遍历,只对传入的根结点进行处理
//先将当前结点的右子树入栈,再将左子树入栈即可
void none_preOrder(NODE *root)
{
    if(root == NULL)
        return;
    stack<NODE*> stk;
    stk.push(root);
    //和层序遍历的队列的思想有点类似
    while(!stk.empty())
    {
        NODE *T = stk.top();
        stk.pop();
        printf("%d ", T->c);
        if(T->rchild != NULL)
            stk.push(T->rchild);
        if(T->lchild != NULL)
            stk.push(T->lchild);
    }

}

//对于中序遍历,从根结点开始一直到最左边的结点
void none_inOrder(NODE *root)
{
    if(root == NULL)
        return;
    stack<NODE*> stk;
    NODE *T = root;
    while(T != NULL || !stk.empty())
    {
        //T可能出现为NULL的情况
        while(T != NULL)
        {
            stk.push(T);
            T = T->lchild;
        }
        if(!stk.empty())
        {
            T = stk.top();
            printf("%d ", T->c);
            stk.pop();
            T = T->rchild;//对右子树实现中序遍历
        }
    }
}

//对于后序遍历,每次访问的结点必然是在访问过它的右结点之后直接访问,右节点必然为空
//一定是紧接着它的右节点后被访问,当然可能为空
//或者没有左右子树才被访问
void none_postOrder(NODE *root)
{
    if(root == NULL)
        return;
    stack<NODE*> stk;
    NODE *cur, *pre = NULL;//注意这个写法
    cur = root;
    while(cur)
    {
        stk.push(cur);
        cur = cur->lchild;
    }//最左入栈
    while(!stk.empty())
    {
        cur = stk.top();
        stk.pop();
        if(cur->rchild == pre || cur->rchild == NULL)//同样满足叶子结点
        {
            printf("%d ", cur->c);
            pre = cur;
        }
        else
        {
            stk.push(cur);
            cur = cur->rchild;//由上面的判断可知rchild一定不为NULL,叶子结点已经被访问过
            while(cur)
            {
                stk.push(cur);
                cur = cur->lchild;
            }

        }

    }
}

//按照从从左到右的顺序,优先向左子树插入,下面的这种方式最终会把数据都插入到根结点的左子树上
//注意&T是引用,实现对T本身的create()而不是仅对T指向的值的修改
void Insert(NODE* &T, int x)
{
    //只有当前为NULL的时候才能插入
    if(T == NULL)
    {
        T = create(x);
        flag = 1 - flag;
        return;
    }
    if(flag == 1)
    {
        Insert(T->lchild, x);
    }
    else if(flag == 0)
    {
        Insert(T->rchild, x);
    }

}

//对于寻找插入位置,也可以用递归的形式,当此时的结点指针为NULL时,就可以调用create(ch)了
//对于查找相应元素并进行修改,也可以进行递归,用先序遍历的方式即可
int main()
{
    NODE* root = NULL;
    loc = 0;
    Insert(root, 1);
    Insert(root, 2);
    Insert(root, 3);
    Insert(root, 4);
    Insert(root, 5);
    printf("先序遍历: \n");
    preOrder(root);
    printf("\n");
    printf("非递归先序遍历: \n");
    none_preOrder(root);
    printf("\n");
    printf("中序遍历: \n");
    inOrder(root);
    printf("\n");
    printf("非递归中序遍历: \n");
    none_inOrder(root);
    printf("\n");
    printf("后序遍历: \n");
    postOrder(root);
    printf("\n");
    printf("非递归后序遍历: \n");
    none_postOrder(root);
    printf("\n");
    printf("层序遍历: \n");
    layerOrder(root);
    printf("\n");
    printf("Tree: ");
    for(int i = 0; i < 5; i++)
        printf("%d ", Tree[i].c);
    return 0;
}

运行结果为:

接下来看题目描述:

题目描述:

二叉树的前序、中序、后序遍历的定义:

前序遍历:对任一子树,先访问跟,然后遍历其左子树,最后遍历其右子树;

中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树;

后序遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根。

给定一棵二叉树的前序遍历和中序遍历,求其后序遍历(提示:给定前序遍历与中序遍历能够唯一确定后序遍历)。

输入:

两个字符串,其长度 n 均小于等于 26

第一行为前序遍历,第二行为中序遍历。二叉树中的结点名称以大写字母表示:ABC....最多 26 个结点。

输出:

输入样例可能有多组,对于每组测试样例,输出一行,为后序遍历的字符串。

样例输入:

ABC

BAC

FDXEAG

XDEFAG

样例输出:

BCA

XEDGAF

题目的解决思路是,从根结点开始,逐渐开始搭建起树的结构,这也是最难的部分,要通过先序遍历和中序遍历的序列规律进行搭建, 在先序遍历中找到根结点,再在中序遍历中找到该结点,该结点左侧的即为该结点的左子树,右侧的即为右子树,再根据先序遍历找到左子树的根结点,继续遍历,直到最后无左子树和右子树递归跳出。下面的程序要注意的是,我们这里没有采用动态产生 node 的方式(malloc),而是预先分配了一个静态数组Tree.

代码为:

#include<cstdio>
#include<iostream>
#include<stdlib.h>
#include<algorithm>
#include<functional>
#include<queue>
#include<string.h>
using namespace std;

struct Node {
	Node *lchild;
	Node *rchild;
	char v;
}Tree[50];

//不用 int loc = 0; 因为会有很多组数据,初始化要在main()中定义
int loc;
Node *create()
{
	Tree[loc].lchild = Tree[loc].rchild = NULL;
	return &Tree[loc++];
}

//全局变量,所以在build函数中不用传入
char str1[30], str2[30];

void postOrder(Node *T)
{
	//先遍历左子树,再遍历右子树,不用else
	if (T->lchild != NULL)
		postOrder(T->lchild);
	if (T->rchild != NULL)
		postOrder(T->rchild);
	printf("%c",T->v);
}

//主要是使每个结点都有对应的左右子树
//s1,e1对应的是先序遍历的起始结束下标,s2,e2对应的是中序遍历的起始结束下标
Node *build(int s1, int e1, int s2, int e2)
{
	Node *T = create();
	T->v = str1[s1];
	int idx;
	for(int i = s2; i <= e2; i++)
		if (str2[i] == str1[s1])
		{
			idx = i;
			break;
		}
	//当有左子树时,再次进行递归,下面的非常重要,最好画图来考虑一下
	//左子树的结点数目是 idx - s2.
	if (idx != s2)
		T->lchild = build(s1 + 1, s1 + idx - s2, s2, idx - 1);
	if (idx != e2)
		T->rchild = build(s1 + idx - s2 + 1, e1, idx + 1, e2);//去掉左子树的数目才是先序遍历右子树的起点
	return T;
}
int main()
{
	//直接用str1,不用取地址
	while (scanf("%s", str1) != EOF)
	{
		scanf("%s", str2);
		loc = 0;
		Node *root = create();
		root = build(0, strlen(str1) - 1, 0, strlen(str2) - 1);
		postOrder(root);
		printf("\n");
	}

	return 0;
}

运行结果为:

ABC
BAC
BCA
FDXEAG
XDEFAG
XEDGAF

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在DevC++实现二叉树的遍可以通过递归或者使用栈来实现。下面是两种常见的二叉树方法的示例代码: 1. 前序遍(Preorder Traversal):先访问根节点,然后递归地遍左子树和右子树。 ```c #include <stdio.h> #include <stdlib.h> // 二叉树结点的定义 struct TreeNode { int data; struct TreeNode* left; struct TreeNode* right; }; // 前序遍函数 void preorderTraversal(struct TreeNode* root) { if (root == NULL) { return; } printf("%d ", root->data); // 访问根节点 preorderTraversal(root->left); // 递归遍左子树 preorderTraversal(root->right); // 递归遍右子树 } int main() { // 构建二叉树 struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode)); root->data = 1; root->left = (struct TreeNode*)malloc(sizeof(struct TreeNode)); root->left->data = 2; root->left->left = NULL; root->left->right = NULL; root->right = (struct TreeNode*)malloc(sizeof(struct TreeNode)); root->right->data = 3; root->right->left = NULL; root->right->right = NULL; // 前序遍叉树 printf("Preorder Traversal: "); preorderTraversal(root); return 0; } ``` 2. 序遍(Inorder Traversal):先递归地遍左子树,然后访问根节点,最后递归地遍右子树。 ```c #include <stdio.h> #include <stdlib.h> // 二叉树结点的定义 struct TreeNode { int data; struct TreeNode* left; struct TreeNode* right; }; // 序遍函数 void inorderTraversal(struct TreeNode* root) { if (root == NULL) { return; } inorderTraversal(root->left); // 递归遍左子树 printf("%d ", root->data); // 访问根节点 inorderTraversal(root->right); // 递归遍右子树 } int main() { // 构建二叉树 struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode)); root->data = 1; root->left = (struct TreeNode*)malloc(sizeof(struct TreeNode)); root->left->data = 2; root->left->left = NULL; root->left->right = NULL; root->right = (struct TreeNode*)malloc(sizeof(struct TreeNode)); root->right->data = 3; root->right->left = NULL; root->right->right = NULL; // 序遍叉树 printf("Inorder Traversal: "); inorderTraversal(root); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值