打印二叉树的边界节点

问题描述


从根节点起,逆时针输出二叉树所有的边界节点:

  • 根节点
  • 每层最左侧节点
  • 每层最右侧节点
  • 底层的所有叶子节点,比如14、15等。

上述的结果是:

1 2 4 7 11 13 14 15 16 12 10 6 3

空间限制是 O ( h ) O(h) O(h),时间限制是 O ( N ) O(N) O(N) h h h是树的高度, N N N是节点的个数。

思路和代码

需要的节点分为三种:

  • 左边界:一层中最左侧的节点,1, 2, 4, 7, 11, 13;为了方便,根节点算左边界
  • 右边界:一层中最右侧的节点,16, 12, 10, 6, 3
  • 底层叶子节点:叶子节点,而且不属于左右边界,14, 15

根据节点的特性,在前序遍历的时候,同一层中最先访问到的节点是左边界节点;最后访问到的是右边界节点。根据这个特性,可以计算左右边界。

如果一个节点没有左右孩子,而且不是左右边界,则该节点一定是底层叶子节点。

问题的难点在于,怎样在 O ( h ) O(h) O(h)的空间和 O ( N ) O(N) O(N)的时间中计算左右边界;因为底层叶子节点求解,在叶子节点中排除左右边界即可。

我们建立一个数组edgeNodes[H][2]H是树的高度。edgeNodes[h][0]表示这层的左边界,edgeNodes[h][0]表示右边界。之后进行前序遍历,当每次访问到第一个节点的时候,就是这层的左边界,此时存储到edgeNodes[h][0]中;同样的,只要遍历到某一层的节点,而且不是左边界节点,就用该节点更新edgeNodes[h][1]中,因为右边界是最后访问到的。注意边界条件,二叉树退化成线性的情况,此时不论左右孩子,都看成是左边界(当然可以看成右边界,但是不要重复)。

给出求解代码:

#include <iostream>
#include <memory>
#include <vector>
#include <array>
#include <algorithm>

struct Node {
    int val;
    std::shared_ptr<Node> left{nullptr}, right{nullptr};
    Node (int n = 0): val(n), left(nullptr), right(nullptr) {}
};

std::vector<std::array<std::shared_ptr<Node>, 2>> edgeNodes;  // 左右边界点
std::vector<std::shared_ptr<Node>> leaves;  // 不在左右边界的叶子节点

// 计算树高
int GetTreeHeight(const std::shared_ptr<Node>& root) {
    if (root == nullptr) {
        return 0;
    }
    return std::max(GetTreeHeight(root->left) + 1, GetTreeHeight(root->right) + 1);
}

// 前序创建二叉树
std::shared_ptr<Node> CreateTreePreOrder() {
    int n;
    std::cin >> n;
    if (n <= 0) {
        return nullptr;
    }
    auto root = std::make_shared<Node>(n);
    root->left = CreateTreePreOrder();
    root->right = CreateTreePreOrder();
    return root;
}

// 设置边界
void SetEdges(const std::shared_ptr<Node>& root, int h) {
    if (root == nullptr) {
        return;
    }
    if (edgeNodes[h][0] == nullptr) {  // 先序遍历,同层左边界总是最先被访问到
        edgeNodes[h][0] = root;
    }
    if (edgeNodes[h][0] != nullptr && root != edgeNodes[h][0]) {  // 先序遍历,同层右边界总是最后访问到
        edgeNodes[h][1] = root;
    }
    SetEdges(root->left, h + 1);
    SetEdges(root->right, h + 1);
}

// 设置叶子节点,不在左右边界的叶子
void SetLeaves(const std::shared_ptr<Node>& root, int h) {
    if (root == nullptr) {
        return;
    }
    if (root->left == nullptr && root->right == nullptr && edgeNodes[h][0] != root && edgeNodes[h][1] != root) {
        leaves.push_back(root);
    }
    SetLeaves(root->left, h + 1);
    SetLeaves(root->right, h +1);
}

void Print() {
    int N = edgeNodes.size();
    // std::cout << "left edges: ";
    for (int i = 0; i < N; ++i) {
        if (edgeNodes[i][0] != nullptr) {
            std::cout << edgeNodes[i][0]->val << " ";
        }
    }
    // std::cout << "\nleaves: ";
    for (const auto &p: leaves) {
        std::cout << p->val << " ";
    }
    // std::cout << "\nright edges: ";
    for (int i = N - 1; i >= 0; --i) {
        if (edgeNodes[i][1] != nullptr) {
            std::cout << edgeNodes[i][1]->val << " ";
        }
    }
}

int main() {
    // 1 2 -1 4 7 -1 -1 8 -1 11 13 -1 -1 14 -1 -1 3 5 9 12 15 -1 -1 16 -1 -1 -1 10 -1 -1 6 -1 -1
    auto root = CreateTreePreOrder();
    edgeNodes.resize(GetTreeHeight(root));  // 分配空间
    SetEdges(root, 0);
    SetLeaves(root, 0);
    Print();
    return 0;
}

程序输出:

left edges: 1 2 4 7 11 13 
leaves: 14 15 
right edges: 16 12 10 6 3
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值