【c/c++】机试中的二叉排序树问题

本文深入探讨了二叉排序树的构建过程及前序、中序、后序遍历方法,通过实例讲解了如何利用递归与非递归方式实现遍历,以及如何判断两个序列是否能构建相同的二叉搜索树。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二叉排序树,又叫二叉搜索树,它的主要特点是对于每个结点,它的左子树上所有结点的值都要小于该结点的值,而右子树所有结点的值都要大于该结点的值,由此我们知道,在对二叉排序树进行中序遍历时,得到的序列一定是一个递增序列。所以这类问题的重点就是如何构造二叉树。

注意create()函数只有在新建一个结点时才需要,类似于new或malloc出一个,分配适当的空间,要注意的是Node* cur, 如果是 cur = stk.top()就没必要new,因为它指向已经存在的结点。

1. 先看问题:

题目描述:

输入一系列整数,建立二叉排序数,并进行前序,中序,后序遍历。

输入:

输入第一行包括一个整数 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

提示:

输入中可能有重复元素,但是输出的二叉树遍历序列中重复元素不用输出。

注://忽略重复的元素。

重复的元素因为代码中只有 x > T->c 和 x < T->c的判断,所以当有重复的值时不会被考虑,直接忽略了。

代码为:

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

struct Node {
	Node *lchild;
	Node *rchild;
	int c;
}Tree[50];

int loc;

Node *create()
{
	Tree[loc].lchild = Tree[loc].rchild = NULL;
	return &Tree[loc++];
}

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

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

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

Node *insert(Node *T, int x)
{
	if (T == NULL)
	{
		T = create();
		T->c = x;
	}
	else if (x < T->c)
		T->lchild = insert(T->lchild, x);
	else if (x > T->c)
		T->rchild = insert(T->rchild, x);
    return T;    //最后返回的是整棵树的根结点指针
}

int main()
{
	int n;
	while (scanf("%d", &n) != EOF)
	{
		loc = 0;
		Node *T = NULL; //二叉排序树的根结点初始化
		for (int i = 0; i < n; i++)
		{
			int x;
			scanf("%d",&x);
			T = insert(T, x);
		}
		printf("先序遍历: ");
		preOrder(T);
		printf("\n");
		printf("中序遍历: ");
		inOrder(T);
		printf("\n");
		printf("后序遍历: ");
		postOrder(T);
		printf("\n");
	}
}

 这里要注意的是使用的是静态结构体数组Tree[50], 如果没有用结构体数组的话,需要使用 malloc 和 free 的组合拳。构建排序二叉树的过程,仍然是一个递归的过程,注意 T = insert(T, x) 中的 T 是整棵树的根结点,返回的也是根结点,但是由于使用的 Node *指针的形式,形参在子函数中的遍历及判断过程中事实上得到了改变,这也是链表问题中,多数使用指针的原因。每次传入的 x ,先与 根结点->c 进行一次比较,然后再与左子树右子树 根结点->c 比较, 直到插入到合适位置,即 T == NULL的位置。

运行结果(可以看到中序遍历是递增的):

5
1 6 5 9 8
先序遍历: 1 6 5 9 8
中序遍历: 1 5 6 8 9
后序遍历: 5 8 9 6 1

上面的Insert方法层层返回,最终返回主函数的是整棵树的根结点,为了统一起见,我们用下面的方法来构造二叉树:

#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);
        return;
    }
    if(x < T->c)//注意题目要求的是左子树小于等于还是小于
    {
        Insert(T->lchild, x);
    }
    else
    {
        Insert(T->rchild, x);
    }

}

//对于寻找插入位置,也可以用递归的形式,当此时的结点指针为NULL时,就可以调用create(ch)了
//对于查找相应元素并进行修改,也可以进行递归,用先序遍历的方式即可
int main()
{
    NODE* root = NULL;
    loc = 0;
    int n;
    int a[20];
    //每次插入的对象都是整棵树的根结点
    scanf("%d", &n);
    for(int i = 0; i < n; i++)
    {
        scanf("%d", &a[i]);
        Insert(root, a[i]);
    }
    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;
}

 运行结果为:

树的形状为:

 

2. 第二个问题,当我们给定 2 个所有元素完全相同的序列,按照上面的方式构建排序二叉树,是不是我们的得到的排序二叉树一定是完全一样的。很可惜,答案是否定的,相同的元素构成的序列,当它们的排列顺序不同时,得到的二叉树不一定相同, 例如,上面的方法一定是将序列第一个元素作为排序二叉树的根结点,当2个序列从第一个结点开始就不相同时,又怎么能要求它们构造的二叉树是完全一致的呢,即使不是第一个元素,假设是第二个,一个为3,一个为4,它们都比根结点5小,分别成为各自左子树的根结点,由此开始它们固定下来,不会随着插入的过程发生变化,2 棵二叉树也是不同的。

问题描述:

题目描述:

判断两序列是否为同一二叉搜索树序列

输入:

开始一个数 n(1<=n<=20) 表示有 n 个需要判断,n= 0 的时候输入结束。接下去一行是一个序列,序列长度小于 10,包含(0~9)的数字,没有重复数字,根据这个序列可以构造出一颗二叉搜索树。接下去的 n 行有 n 个序列,每个序列格式跟第一个序列一样,请判断这两个序列是否能组成同一颗二叉搜索树。

输出:

如果序列相同则输出 YES,否则输出 NO

样例输入:

2

5 6 7 4 3 2

5 4 3 2 6 7

5 7 6 3 4 2

0

样例输出:

YES

NO

判断 2 棵二叉树是否完全一样的方法是,如果它们的先序和中序或者中序和后序(一定要包括中序)序列元素排列相同时,则它们必然是同一棵二叉树,即树的形状完全是一致的。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值