题目描述
二叉搜索树(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;
}