「1064」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.

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

Ω

给出一组数,输出其完全(最后一层节点从左至右无间隔排列)有序二叉数的层次遍历。

首先我们对所有数字进行排序得到序列 ,我们只要按照层次遍历的顺序依次求出每个数在 中的索引即可,而索引值即为小于该数的数字个数。

对于根节点很显然其索引值即为左子树的节点个数,而对于其他节点的索引值计算我们可以分为两部分:

  1. 以该节点为根节点的子树,其中小于该数的节点个数依然是左子树节点个数

  2. 其余部分(CBT-上述子树),这里我们需要分两种情况讨论:

    • 该节点P为某节点的左子节点,那么小于该数的节点个数就是其父节点F的Part 2值,即父节点F的其余部分小于父节点值的节点个数。注意到,P和F的其余部分相比仅多了F本身和F的右子树,即P的兄弟子树,而右子树上的节点和F的值都>P,因此两者是相等的

    • 该节点是某节点的右子节点,那么小于该数的节点个数是小于其父节点F的节点个数+1。注意到,父节点F的其余部分与该节点其余部分仅相差F本身与F的左子树,而F的左子树是F的Part 1值,因此将其与F的其余部分相加即为小于F的节点个数,最后再算上F本身+1

首先我们可以计算出每个节点中左右子树的节点个数(自底向上,逐层遍历),用pair<int,int>来存储,然后根据上述分析不难发现计算索引时我们只需用到左子树的节点个数,于是pair.second(原本存储右子树节点个数的部分)就可以用存储Part 2的值。注意到Part 2在计算节点本身索引时不需要用到(子节点需要),所以在计算完索引后顺便更新其pair.second。


🐎

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
    int n, level = 1, prod = 2;
    cin >> n;
    while (prod < n + 1)
    {
        prod *= 2;
        level += 1;
    }
    vector<pair<int, int>> node_num;
    node_num.resize(prod - 1, pair(0, -1));
    for (int i = prod / 2 - 1; i < n; ++i)
        node_num[i] = std::move(pair(0, 0));
    for (int i = prod / 4; i > 0; i /= 2)
        for (int j = i - 1; j < 2 * i - 1; ++j)
            node_num[j] = std::move(pair(node_num[2 * j + 1].first + node_num[2 * j + 1].second + 1,
                                         node_num[2 * j + 2].first + node_num[2 * j + 2].second + 1));
    vector<int> keys(n);
    for (auto &k: keys)
        cin >> k;
    sort(keys.begin(), keys.end());
    cout << keys[node_num[0].first];
    node_num[0].second = 0;
    for (int i = 0; i < n - 1; ++i)
    {
        int idx = (i % 2 ? node_num[i / 2].first + 1 : 0) + node_num[i / 2].second + node_num[i + 1].first;
        cout << " " << keys[idx];
        node_num[i + 1].second = idx - node_num[i + 1].first;
    }
}

Π

上述的思考模式又是一种逆推的过程,多少有点反人类。仔细观察能够发现有序二叉树的中序遍历即为排序结果,那么题目就能转化为已知二叉树的中序遍历如何求其层次遍历的问题。那么我们就顺势而为,模拟一遍中序遍历,然后在模拟过程中顺便计算每个节点在层次遍历中的索引,然后根据索引进行赋值即可。简单地说,就是站在层次遍历的角度去做中序遍历。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
int n;
vector<int> level, keys;

void InOrder(int node)
{
    if (node >= n) return;
    static int idx = 0;
    InOrder(2 * node + 1);
    level[node] = keys[idx++];
    InOrder(2 * node + 2);
}

int main()
{
    cin >> n;
    keys.resize(n);
    level.resize(n);
    for (auto &k: keys)
        cin >> k;
    sort(keys.begin(), keys.end());
    InOrder(0);
    for (int i = 0; i < level.size(); ++i)
        cout << (i == 0 ? "" : " ") << level[i];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值