1064 Complete Binary Search Tree (30 分)
A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties:
- The left subtree of a node contains only nodes with keys less than the node's key.
- The right subtree of a node contains only nodes with keys greater than or equal to the node's key.
- Both the left and right subtrees must also be binary search trees.
A Complete Binary Tree (CBT) is a tree that is completely filled, with the possible exception of the bottom level, which is filled from left to right.
Now given a sequence of distinct non-negative integer keys, a unique BST can be constructed if it is required that the tree must also be a CBT. You are supposed to output the level order traversal sequence of this BST.
Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer N (≤1000). Then N distinct non-negative integer keys are given in the next line. All the numbers in a line are separated by a space and are no greater than 2000.
Output Specification:
For each test case, print in one line the level order traversal sequence of the corresponding complete binary search tree. All the numbers in a line must be separated by a space, and there must be no extra space at the end of the line.
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
参考大佬博客:https://blog.csdn.net/richenyunqi/article/details/78868563
本题思路
方法一: 利用完全搜索二叉树的性质
n为结点个数
1.先将输入的n个结点值排序得到中序排列,
2. 通过log(n + 1) 向下取整 (floor() 为向下取整, ceil为向上取整) 得到除去最后一层的高度h eg:n=7的结点高度为3 (因为根节点只有一个值 所以需要 log n+1 除去最后一层高度)
3.得到最后一层的结点数: n - (pow(2, h) - 1); (可能只存在左结点, 也存在右结点)
4. 在测试样例1 中序排列中 : 0 1 2 3 4 5 6 7 8 9 只要知道了 左结点的个数 num; 通过(left(中序遍历结点最左边) + num 就能确定 root结点,)找到了根结点后 然后不断递归 先左子树, 后右子树, 设定一个index 记录根的下标 方便 层序输出
使 temp2 = (pow(2,h) - 1 ) / 2
5. 关键就如何求出左子树结点个数 : 此时 temp2 得到最后一层左子树能够存放的最大结点数(最后一层能够存储最大容量的一半), 通过min(temp2 , n - (pow(2, h) - 1) )得到此时最后一层能够存放的左子树元素, temp2 -1 可以得到除去最后一层的左子树个数 : 因此左结点个数和sum为: sum =temp2 - 1 + min(temp2 , n - (pow(2, h) - 1) )
具体代码
:
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
int n;
vector<int> in,level;
void levelOrder(int start, int end, int index){
if(start > end) return;
int sum = end - start + 1;
int height = (int)floor(log((sum) + 1) / log(2)); //还没强转 log在cmath库中
int temp1 = sum - (int)(pow(2, height) - 1); //最后一层的结点数量
int temp2 = pow(2, height) / 2; //最后一层能够容纳的最大结点数量的一半
temp1 = min(temp1, temp2); //取最小是因为temp1可能包含右结点,现在只需要算出左结点
temp1 = start + temp1 + temp2 - 1; //此时左子树结点总数为(temp1 + temp2 - 1) 同时加上start(left)就是中序遍历中根结点的下标
level[index] = in[temp1];
levelOrder(start, temp1 - 1, 2 * index + 1);
levelOrder(temp1 + 1, end, 2 * index + 2);
}
int main(){
scanf("%d", &n);
in.resize(n); //在全局遍历中 用(n)也不能scanf输入 这里注意 只有用resize容器具有具体长度时 才能用scanf赋值
level.resize(n);
for(int i = 0; i < n; i++){
scanf("%d", &in[i]);
}
sort(in.begin(), in.end());
levelOrder(0, n-1, 0);
for(auto i = 0; i < n; i++){
if(i != 0) printf(" ");
printf("%d", level[i]);
}
return 0;
}
方法二:
方法一是通过顺序查找到每个 根 结点将每个结点的下标索引放入level数组中,
更简便的方法是 直接通过 中序遍历 将中序遍历中的每个元素都加入到level数组 中 并不需通过求出高度,找到左结点的个数 先序遍历找到每个根结点
具体代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n,position=0;
vector<int> level, in;
void levelOrder(int index){
if(index > n-1) return; //注意这里是n-1 因为根节点的下标是0 那么最后一个结点的下标为n-1
levelOrder(index * 2 + 1); // 完全二叉搜索树中中序遍历中 第一个结点会是最左边的结点
level[index] = in[position++];
levelOrder(index * 2 + 2);
}
int main(){
scanf("%d", &n);
level.resize(n);in.resize(n);
for(int i = 0; i < n; i++){
scanf("%d", &in[i]);
}
sort(in.begin(),in.end());
levelOrder(0);
for(int i = 0; i < n; i++){
if(i != 0) printf(" ");
printf("%d", level[i]);
}
return 0;
}