2018.5.8
题目的意思很简单,给定一个任意的输入序列,把这个序列建成一棵完全二叉搜索树(既是一棵完全二叉树,又是一棵二叉搜索树),并且层次遍历打印出来。但是实现起来却并不那么轻松,完全二叉树一般用数组存储比较方便,而二叉搜索树用链表存储比较方便。
之前我们在《#数据结构与算法学习笔记#PTA10:层次遍历叶节点(JAVA)》中写到了如何利用队列将链表存储的二叉树进行层次遍历。因此最一开始,笔者就先用链表试着实现了一下,发现做一棵二叉搜索树简单,每个结点插入的时候按照大小比较的规则插入就好,但是要想将一棵二叉搜索树转变成一棵完全二叉树,转换方式极其繁琐,基本上每个结点插入都要将整棵树的结构重构。因此,放弃用链表存储,转而改用数组存储。
对于某一指定序列来说,无论先后顺序如何,其建成的完全二叉搜索树肯定都是一模一样的。如果能提前发现这点,就更应该直接用数组进行存储了(并且更方便进行层次遍历打印)。基本思想是,先将输入序列从小到大进行排——再计算该树左子树的结点数(给出该树总结点数total,必能算出左子树的结点数和其根节点所在位置,计算方法如注释)——最后依次在数组中层序装入个根节点值——递归装入每一个左右子树根节点。基本思想同《#数据结构与算法学习笔记#PTA11:先序遍历+中序遍历转后序遍历/二叉树非递归遍历/二叉树栈遍历(JAVA)》中的递归思想。
题目要求:
A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties:
1.The left subtree of a node contains only nodes with keys less than the node's key.
2.The right subtree of a node contains only nodes with keys greater than or equal to the node's key.
3.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.
实现代码:
// CompleteBinarySearchTree.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include <vector> #include <algorithm> #include <cmath> using namespace std; //用于sort递增排序的比较函数 bool Compare(const int &a, const int &b); //根据顺序序列创建完全二叉搜索树 void Create(vector<int>& input, vector<int>& tree, int left, int right, int root); //获取该结点左子树结点数 int GetLeftNum(int total); void Print(vector<int>& tree); int main() { vector<int> input; int num; cin >> num; int element; for (int i = 0; i < num; i++) { cin >> element; input.push_back(element); } //从小到大排序 sort(input.begin(), input.end(), Compare); //Print(input); vector<int> tree(num); Create(input, tree, 0, num - 1, 0); Print(tree); system("pause"); return 0; } bool Compare(const int &a, const int &b) { return a < b; } void Create(vector<int>& input, vector<int>& tree, int left, int right, int root) { //该树总结点数 int total = right - left + 1; if (total == 0)return; //该树左子树的结点数 int leftnum = GetLeftNum(total); tree[root] = input[left + leftnum]; //左子树根节点在tree序列的下标 int leftroot = root * 2 + 1; //右子树根节点在tree序列的下标 int rightroot = leftroot + 1; //递归创建其左右子树 Create(input, tree, left, left + leftnum - 1, leftroot); Create(input, tree, left + leftnum + 1, right, rightroot); } int GetLeftNum(int total) { int n = log(total) / log(2); //完美二叉树层数(除最底层) int leftnum = total - (pow(2, n) - 1); //所有最底层叶子结点 int leftmax = pow(2, n - 1); //左子树最底层最大结点 int other = pow(2, n - 1) - 1; //左子树枝干分支结点 //若最底层叶子结点全在左子树,则返回最底层叶子结点+其枝干分支节点 if (leftnum < leftmax) { return leftnum + other; } //若最底层叶子结点存在于左右子树,则返回左子树最底层最大结点+其枝干分支结点 else if(leftnum >= leftmax){ return leftmax + other; } } void Print(vector<int>& tree) { cout << tree[0]; for (int i = 1; i < tree.size(); i++) { cout << " " << tree[i]; } }