浙江大学数据结构MOOC-课后习题-第四讲-树6 Complete Binary Search Tree

题目汇总
浙江大学数据结构MOOC-课后习题-拼题A-代码分享-2024

题目描述

二叉搜索树(BST)是一个递归定义的二叉树,它具有以下属性:

  • 一个节点的左子树只包含键值小于该节点键值的节点。
  • 一个节点的右子树只包含键值大于或等于该节点键值的节点。
  • 左右子树也必须是二叉搜索树。

完全二叉树(CBT)是一个完全填充的树,除了最底层可能不是从左到右填充的。

现在,给定一个由不同的非负整数键组成的序列,如果要求树也必须是完全二叉树,那么可以构建一个唯一的BST。你需要输出这个BST的层序遍历序列。

输入规范:
每个输入文件包含一个测试用例。对于每个案例,第一行包含一个正整数N(≤1000)。然后,接下来的一行给出了N个不同的非负整数键。一行中的所有数字由空格分隔,并且不大于2000。

输出规范:
对于每个测试用例,打印一行对应的完全二叉搜索树的层序遍历序列。一行中的所有数字必须由空格分隔,并且行尾没有多余的空格。

Sample Input:

10
1 2 3 4 5 6 7 8 9 0

Sample Output:

6 3 8 1 5 7 9 0 2 4

思路分析

①首先先想好树的数据结构用什么?
由于题目中并不设计删除、查找的操作,从这一层面考虑用链表或数组均可。
对于非完全二叉树,若用数组存储,则数组需要为空节点分配空间,那么会造成空间浪费。
但是对于本题来说,由于是完全二叉搜索树,不存在空间浪费的问题;再加上题目要求最后进行层次遍历,而用数组存储树时,恰好节点的在数组中的存储顺序就是层次遍历的次序,因此本题选用数组来存储树

②核心算法
兄弟们首先要明确一点:当节点数给定的时候,那么完全二叉树的形态也一定是固定的。(不信可以动手试试hhh

根据二叉搜索树的特性,左子树上的所有值都要比根节点的值小。如果将二叉搜索树的所有节点值按从小到大的顺序排列,当我们知道左子树上的节点总数n时,那我们由此得知根节点是位于序列中的第n+1个元素,那么就将该元素放入根节点中。
然后对左右子树分别递归地执行以上操作…

③关于二叉树的性质

性质1:树的高度h = log2^N + 1向下取整
性质2:二叉树的第i层,最多有2^(i-1)个结点
性质3:深度为k的二叉树,最多有2^k-1个结点
#include <set>
#include <cmath>
#include <vector>
#include <cassert>
#include <iostream>
#include <algorithm>

#define MAXSIZE 1000
int T[MAXSIZE];				//存储结果树
std::vector<int> treeInOrder;	//存储排序后的输入序列
int index = -1;

int getLeftSum(int left, int right)
{	
	if (left == right) return 0;
	int N, height, i, j, k, l;
	//节点总数
	N = right - left + 1;
	if (N == 1) return 0;
	else if (N == 2 || N == 3) return 1;
	
	/* 性质:树的高度h = log2^N + 1向下取整 */
	height = int(std::log(N) / std::log(2) + 1);

	/* 性质:二叉树的第i层,最多有2^(i-1)个结点 */
	/* 第h - 1层的节点数i = 2 ^ (h - 2),左右子树分别有 i / 2个节点	  */
	i = std::pow(2, height - 2);
	
	/* 性质:深度为k的二叉树,最多有2^k-1个结点 */
	//前h-1层的总共节点数j = 2^(h-1) - 1;		前2层共有j==3个
	j = std::pow(2, height - 1) - 1;
	
	//第h层的节点数 = 总节点数 - 前h-1层的节点数		第3层有:1个
	k = N - j;
	
	//第h-1层的左子树最多可存放的叶子结点数 = i / 2 * 2 == i	最后一层
	if (i <= k) l = (j - 1) / 2 + i;
	else l = (j - 1) / 2 + k;

	return l;	//返回左子树节点总数
}


//如果仅仅设置left和right为入口参数,那么T中存储的顺序就是先序遍历的顺序了
//因此要给该函数指明下一个根节点在T中存放的位置
void buildTree(int left, int right, int rootPos)
{	

	int R;	
	int n = right - left + 1;
	if (n == 0) return;
	else
	{	
		//获取根节点在序列中的索引号,将根节点存入T中
		R = left + getLeftSum(left, right);
		T[rootPos] = treeInOrder[R];
		buildTree(left, R - 1, rootPos * 2 + 1);
		buildTree(R + 1, right, rootPos * 2 + 2);
	}
}
int main()
{
	int N, R;
	std::cin >> N;
	assert(N > 0);
	for(int i = 0; i < N; i++)
	{
		std::cin >> R;
		treeInOrder.push_back(R);
	}
	//将元素从小到大排序
	std::sort(treeInOrder.begin(), treeInOrder.end());
	
	buildTree(0, treeInOrder.size() - 1, 0);
	
	for (int i = 0; i < N; i++)
	{
		if (i == 0) std::cout << T[i];
		else std::cout << ' ' << T[i];
	}

	return 0;
}
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值