【树】Complete Binary Search Tree

【树】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. 采用什么样的存储结构:题目要求构建完全二叉搜索树,可以考虑使用数组,因为不会浪费空间;题目要求层序遍历输出,如果用链表存储,层序遍历较为复杂,如果采用数组存储,直接按序输出即可。综合以上两点,采用数组存储。
  2. 怎样通过给定序列构建完全二叉搜索树:完全二叉搜索树的特点是左子树中所有结点键值均小于根结点,右子树大于根结点,因此将序列排序,计算出左子树的结点数,就可以确定根结点,然后递归左右子树就可以构建出完全二叉搜索树。
  3. 怎样找到树根:因为树的结点数是给定的,因此可以根据完全二叉树的性质求出左子树的结点数,然后在从小到大排好序的序列中确定出根结点。
  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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百栗.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值