【树】Complete Binary Search Tree
题目要求:
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.
输入格式:
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.
输出格式:
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.
输入样例:
10
1 2 3 4 5 6 7 8 9 0
输出样例:
6 3 8 1 5 7 9 0 2 4
解题思路:
- 采用什么样的存储结构:题目要求构建完全二叉搜索树,可以考虑使用数组,因为不会浪费空间;题目要求层序遍历输出,如果用链表存储,层序遍历较为复杂,如果采用数组存储,直接按序输出即可。综合以上两点,采用数组存储。
- 怎样通过给定序列构建完全二叉搜索树:完全二叉搜索树的特点是左子树中所有结点键值均小于根结点,右子树大于根结点,因此将序列排序,计算出左子树的结点数,就可以确定根结点,然后递归左右子树就可以构建出完全二叉搜索树。
- 怎样找到树根:因为树的结点数是给定的,因此可以根据完全二叉树的性质求出左子树的结点数,然后在从小到大排好序的序列中确定出根结点。
- 怎样确定左子树的结点数:
首先给出二叉树的三个性质:
性质1:结点数为 n 的完全二叉树高度 h = log 2 n h = \log_2 n h=log2n向下取整 + 1
性质2:高度为 h 的完美二叉树结点数 n n n = 2h - 1
性质3:完全二叉树第 h 层的结点数 n 最多为 n n n = 2h - 1
具体方法如下:
步1:已知完全二叉树结点数 n,求得 h = log 2 n h = \log_2 n h=log2n向下取整 + 1
步2:设完全二叉树最后一层结点数 rest,rest = n - (2h - 1 - 1)
步3:设完全二叉树左子树最后一层结点数 lRest,lRest = min(rest,2h - 2)
步4:设完全二叉树左子树结点数 nl,nl = 2h - 2 - 1 + lRest
注意:
C语言库函数中没有log以2为底,只有log以10为底和ln,因此需要用到换底公式。
完整程序:
/*
【解题思路】
题目要求根据给出的序列构建完全二叉搜索树,然后按层序遍历输出树的结点。因为二叉搜索树的左子树中所有结点的键值均小于树根,右子树
大于树根,因此需要将序列从小到大排序,找到树根,则树根元素左边的序列(比树根小的序列)就构成了左子树,树根元素右边的序列就构成
了右子树。然后对左右子树进行递归操作分别找到左右子树的根结点,一直递归则可以构建出一个完全二叉树。
如何找到树根:因为树结点数是确定的,就可以根据完全二叉树的性质确定出左右子树的结点树,那自然就可以在排序后的序列中找到树根。
如何存储树:因为要进行树的层次遍历输出,如果采用链表则比较复杂,采用数组直接按顺序输出即可。因为是完全二叉树,因此没有多少空间
浪费。
如何求一个n个结点的完全二叉树的左子树结点:根据公式可以求得该二叉树的高度 h = log以2为底n向下取整加1,设二叉树最后一层的结点数
为 rest,则 n = rest + (高度为 h -1 层的完美二叉树的结点 ),从而解出rest,设左子树最后一层的结点数为 lRest,则 lRest = 2的 h -2
次方 和 rest 中的最小值,则左子树的结点数 nl = (高度为 h - 2的完美二叉树结点) + lRest
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h> // log10(n)函数
#define MaxSize 1000
int seq[MaxSize],tree[MaxSize]; // seq:输入的序列;tree:实现树的存储(注意不用从1开始,从0开始存储即可)
int Compare(const void *p1,const void *p2); // qsort函数的排序函数
void CreateTree(int sLeft,int sRight,int tRoot); // 建树(存储方式——数组)
int GetLeftLength(int n); // 求结点数为n的树的左子树的结点数
int main()
{
int i,N;
scanf("%d",&N);
for(i = 0;i < N;i ++)
scanf("%d",&seq[i]);
qsort(seq,N,sizeof(int),Compare); // 数组首地址;元素个数;元素占用字节数;排序函数名
CreateTree(0,N - 1,0);
for(i = 0;i < N - 1;i ++) {
printf("%d ",tree[i]);
}
printf("%d",tree[i]);
return 0;
}
int Compare(const void *p1,const void *p2) {
return (*(int*)p1 - *(int*)p2); // 从小到大排序
}
void CreateTree(int sLeft,int sRight,int tRoot) // 序列最左侧元素的下标;序列最右侧元素的下标;求得的树根在tree[]中的下标
{
/*初始调用 CreateTree(0,N - 1,0)*/
int n,nl,leftRoot,rightRoot; // 序列元素个数;左子树结点数;根结点左孩子在tree[]中下标;根结点右孩子在tree[]中下标
n = sRight - sLeft + 1;
if(n == 0)
return; // 递归结束条件
nl = GetLeftLength(n); // 计算出n个结点的树其左子树有多少个结点
tree[tRoot] = seq[sLeft + nl]; // 找到根结点,将其放在tree[]的合适位置
leftRoot = tRoot * 2 + 1; // 根结点的左孩子在tree[]的位置(因为从0开始存储,因此需要加1)
rightRoot = leftRoot + 1; // 根结点的右孩子在tree[]的位置
CreateTree(sLeft,sLeft + nl - 1,leftRoot); // 左子树递归
CreateTree(sLeft + nl + 1,sRight,rightRoot); // 右子树递归
}
int GetLeftLength(int n) // n为二叉树的结点数
{
/*n个结点的完全二叉树的深度为log以2为底n向下取整 + 1*/
int h; // 二叉树高度
int rest; // 最后一层的结点数(有可能满有可能没满,有可能甚至填不满左子树)
int lRest; // 左子树最后一层的结点数
int nl; // 左子树结点总数
h = log10(n) / log10(2) + 1; // C语言中没有log以2为底的函数,因此使用换底公式
rest = n - (pow(2,h - 1) - 1);
lRest = rest < pow(2,h - 2) ? rest : pow(2,h - 2);
nl = pow(2,h - 2) - 1 + lRest;
return nl;
}