二叉排序树及遍历
题目描述
输入一系列整数,建立二叉排序树,并进行前序,中序,后序遍历。
输入描述:
输入第一行包括一个整数n(1<=n<=100)。
接下来的一行包括n个整数。
输出描述
可能有多组测试数据,对于每组数据,将题目所给数据建立一个二叉排序树,并对二叉排序树进行前序、中序和后序遍历。
每种遍历结果输出一行。每行最后一个数据之后有一个空格。
输入中可能有重复元素,但是输出的二叉树遍历序列中重复元素不用输出。
示例:
输入:
5
1 6 5 9 8
输出:
1 6 5 9 8
1 5 6 8 9
5 8 9 6 1
我们先将题目分解开来,首先第一步,要求根据所给的数据建立一个二叉排序树。
建立二叉排序树
首先新建一个根结点,然后循环向其中插入新结点即可。实现代码如下:
//给出树的结点的定义
typedef struct BNode{
int data; //数据
struct BNode *left; //左指针
struct BNode *right; //右指针
}BNode;
//定义插入函数实现二叉排序树的建立
BNode *Insert(BNode *root, int x){ //向root指向的排序树中插入数据值为x的结点
if(root==NULL){ //如果是空树
root = (BNode*)malloc(sizeof(BNode)); //申请一个新结点的空间
root -> data = x; //结点的值为x
root -> left = NULL;
root -> right = NULL; //左右子树均为空
return root; //返回根节点
}
if(x < root->data){
//如果插入值小于根结点的值,那么就插入到左子树中
//返回值为指向根结点的左子树的指针
root -> left = Insert(root->left, x);
}
if(x > root->data){
//如果插入值大于根结点的值,那么就插入到右子树中
//返回值为指向根结点的右子树的指针
root -> right = Insert(root->right, x);
}
return root; //最后返回指向根结点的指针
}
二叉树的三种遍历方式
前序遍历
所谓前序遍历中的前,指的是根结点在前,即按照根左右的顺序遍历。
void preOrder(BNode *root){
printf("%d ",root->data); //输出根结点的值
if(root->left != NULL){
preOrder(root->left); //遍历左子树
}
if(root->right != NULL){
preOrder(root->right); //遍历右子树
}
}
中序遍历
同理,中序遍历是指根结点在中间进行遍历,即按照左根右的顺序进行遍历。
void inOrder(BNode *root){
if(root->left != NULL){ //遍历左子树
inOrder(root->left);
}
printf("%d ",root->data); //遍历根结点
if(root->right != NULL){ //遍历右子树
inOrder(root->right);
}
}
后序遍历
同理,后序遍历指的是根结点在最后进行遍历,即按照左右根的顺序进行遍历。
void postOrder(BNode *root){
if(root->left != NULL){ //遍历左子树
postOrder(root->left);
}
if(root->right != NULL){ //遍历右子树
postOrder(root->right);
}
printf("%d ",root->data); //遍历根结点
}
题目实现完整代码
#include<stdio.h>
#include<malloc.h>
typedef struct BNode{
int data;
struct BNode *left;
struct BNode *right;
}BNode;
//插入新结点,建立二叉排序树
BNode *Insert(BNode *root, int x){
if(root == NULL){
root = (BNode *)malloc(sizeof(BNode));
root -> data = x;
root -> left = NULL;
root -> right = NULL;
return root;
}
if(x < root->data){
root->left = Insert(root->left, x);
}
if (x > root->data){
root->right = Insert(root->right, x);
}
return root;
}
//前序遍历
void preOrder(BNode *root){
printf("%d ",root->data);
if(root->left != NULL){
preOrder(root->left);
}
if(root->right != NULL){
preOrder(root->right);
}
}
//中序遍历
void inOrder(BNode *root){
if(root->left != NULL){
inOrder(root->left);
}
printf("%d ",root->data);
if(root->right != NULL){
inOrder(root->right);
}
}
//后序遍历
void postOrder(BNode *root){
if(root->left != NULL){
postOrder(root->left);
}
if(root->right != NULL){
postOrder(root->right);
}
printf("%d ",root->data);
}
int main(){
int n;
while(scanf("%d",&n) != EOF){
BNode *root = NULL;
for(int i=0; i<n; i++){
int x;
scanf("%d ",&x);
root = Insert(root,x);
}
preOrder(root);
printf("\n");
inOrder(root);
printf("\n");
postOrder(root);
printf("\n");
}
return 0;
}
后续:二叉树的层序遍历
其实,对于二叉树,还有一种遍历方式——层序遍历,即对每一层按顺序进行遍历。虽然此题并未提及,但为了知识的系统化,在此总结。
在层序遍历中,需要用到一个很重要的数据结构——队列,对于队列,C++的STL(标准模板库)中提供了队列模板,对于其实现细节,我们不用过多关注,只需要了解它在程序中的用法即可。
关于队列模板的详细用法,请参照STL–queue
对于层序遍历,其算法思想如下:
- 借助队列,首先定义一个队列,其元素类型为结点
- 如果根结点非空,将其入队
- 当队列不为空时,队首结点出队,访问队首结点,若它有左子树,则将左子树的根结点入队;若它有右子树,则将右子树的根结点入队。如此往复,直到队列为空。
#include<iostream>
#include<queue>
void levelOrder(BNode *root){
queue<BNode*> myQueue; //定义一个队列,这个队列中存储的元素类型是结点
if(root != NULL){
myQueue.push(root); //入队
}
while(!myQueue.empty()){ //当队不为空时
BNode *p = myQueue.front(); //读出队首元素
myQueue.pop(); //出队
cout<<p->data; //输出元素
if(p->left != NULL){ //如果它有左子树,将左子树的根结点入队
myQueue.push(p->left);
}
if(p->right != NULL){ //如果它有右子树,将右子树的根结点入队
myQueue.push(p->right);
}
}
}
当然,如果想要做到统一,那么直接将上面C语言的代码头文件改成C++的头文件即可,不要忘了对于结构体的定义,C语言和C++有区别,在C++中去掉typedef struct BNode中的typedef即可,或者声明一个变量BNode Bnode; ,然后用Bnode进行访问。