如何删除具有单个子节点的树节点的子节点
How to remove children of tree nodes with single child
我有一个数组用于树的预序遍历(节点值是深度值)。我想做的就是通过删除只有一个子节点的内部节点的子节点来最小化树。
举个例子(最大深度= 3的树)
problem visualized here
输入数组:[0, 1, 2, 3, 3, 1, 2, 3]
输出数组:[0, 1, 2, 2, 1]
算法应该如何?
一个简单的 O(nlog(n)) 平均情况算法产生于通过分而治之的方法解决问题。
从 input_level = 0
、output_level=0
、left=0
、right=n-1
开始。
在每个递归步骤中,在范围 [left
、right
] 中计算输入数组 A
中值为 input_level+1
的元素。这些是当前节点的子节点。如果没有这样的元素,则打印 output_level
和 return。如果只有一个这样的元素,"delete" 当前节点(即不打印它),将 left
增加 1,并递归调用该函数。如果有两个或更多这样的元素,则打印 output_level
,将 output_level
加 1,然后递归地将函数应用于子元素划定的每个间隔。 递归调用时总是增加input_level
。
对于示例输入 A=[0, 1, 2, 3, 3, 1, 2, 3]
,首先算法会在索引 1 和 5 处找到值为 1 的元素。然后它会打印 0,将 output_level
和 current_level
增加1,并递归调用自身两次:范围 [1, 4] 和 [5, 7].
这个的复杂度在最坏的情况下是 O(n2)(对于实际上是列表的退化树),但是 O(nlog(n))平均而言,随机 n 叉树的高度为 O(log(n)).
以下算法在 O(N) 中运行。这次我猜对了。
#include <algorithm>
#include <iostream>
#include <stack>
#include <tuple>
#include <utility>
#include <vector>
void mark_nodes(const std::vector<unsigned>& tree,
std::vector<bool>& mark) {
// {depth, index, mark?}
using triple = std::tuple<unsigned, unsigned, bool>;
std::stack<triple> stk;
stk.push({0, 0, false});
for (auto i = 1u; i < mark.size(); ++i) {
auto depth = tree[i];
auto top_depth = std::get<0>(stk.top());
if (depth == top_depth) {
stk.pop();
if (stk.size()) std::get<2>(stk.top()) = false;
continue;
}
if (depth > top_depth) {
std::get<2>(stk.top()) = true;
stk.push({depth, i, false});
continue;
}
while (std::get<0>(stk.top()) != depth) {
mark[std::get<1>(stk.top())] = std::get<2>(stk.top());
stk.pop();
}
mark[std::get<1>(stk.top())] = std::get<2>(stk.top());
stk.pop();
if (stk.size()) std::get<2>(stk.top()) = false;
stk.push({depth, i, false});
}
mark[0] = false;
}
std::vector<unsigned> trim_single_child_nodes(
std::vector<unsigned> tree) {
tree.push_back(0u);
std::vector<bool> mark(tree.size(), false);
mark_nodes(tree, mark);
std::vector<unsigned> ret(1, 0);
tree.pop_back();
mark.pop_back();
auto max_depth = *std::max_element(tree.begin(), tree.end());
std::vector<unsigned> depth_map(max_depth + 1, 0);
for (auto i = 1u; i < tree.size(); ++i) {
if (mark[i]) {
if (tree[i] > tree[i - 1]) {
depth_map[tree[i]] = depth_map[tree[i - 1]];
}
} else {
if (tree[i] > tree[i - 1]) {
depth_map[tree[i]] = depth_map[tree[i - 1]] + 1;
}
ret.push_back(depth_map[tree[i]]);
}
}
return ret;
}
int main() {
std::vector<unsigned> input = {0, 1, 2, 3, 3, 1, 2, 3};
auto output = trim_single_child_nodes(input);
for (auto depth : output) {
std::cout << depth << ' ';
}
}
我有一个数组用于树的预序遍历(节点值是深度值)。我想做的就是通过删除只有一个子节点的内部节点的子节点来最小化树。
举个例子(最大深度= 3的树)
problem visualized here
输入数组:[0, 1, 2, 3, 3, 1, 2, 3]
输出数组:[0, 1, 2, 2, 1]
算法应该如何?
一个简单的 O(nlog(n)) 平均情况算法产生于通过分而治之的方法解决问题。
从 input_level = 0
、output_level=0
、left=0
、right=n-1
开始。
在每个递归步骤中,在范围 [left
、right
] 中计算输入数组 A
中值为 input_level+1
的元素。这些是当前节点的子节点。如果没有这样的元素,则打印 output_level
和 return。如果只有一个这样的元素,"delete" 当前节点(即不打印它),将 left
增加 1,并递归调用该函数。如果有两个或更多这样的元素,则打印 output_level
,将 output_level
加 1,然后递归地将函数应用于子元素划定的每个间隔。 递归调用时总是增加input_level
。
对于示例输入 A=[0, 1, 2, 3, 3, 1, 2, 3]
,首先算法会在索引 1 和 5 处找到值为 1 的元素。然后它会打印 0,将 output_level
和 current_level
增加1,并递归调用自身两次:范围 [1, 4] 和 [5, 7].
这个的复杂度在最坏的情况下是 O(n2)(对于实际上是列表的退化树),但是 O(nlog(n))平均而言,随机 n 叉树的高度为 O(log(n)).
以下算法在 O(N) 中运行。这次我猜对了。
#include <algorithm>
#include <iostream>
#include <stack>
#include <tuple>
#include <utility>
#include <vector>
void mark_nodes(const std::vector<unsigned>& tree,
std::vector<bool>& mark) {
// {depth, index, mark?}
using triple = std::tuple<unsigned, unsigned, bool>;
std::stack<triple> stk;
stk.push({0, 0, false});
for (auto i = 1u; i < mark.size(); ++i) {
auto depth = tree[i];
auto top_depth = std::get<0>(stk.top());
if (depth == top_depth) {
stk.pop();
if (stk.size()) std::get<2>(stk.top()) = false;
continue;
}
if (depth > top_depth) {
std::get<2>(stk.top()) = true;
stk.push({depth, i, false});
continue;
}
while (std::get<0>(stk.top()) != depth) {
mark[std::get<1>(stk.top())] = std::get<2>(stk.top());
stk.pop();
}
mark[std::get<1>(stk.top())] = std::get<2>(stk.top());
stk.pop();
if (stk.size()) std::get<2>(stk.top()) = false;
stk.push({depth, i, false});
}
mark[0] = false;
}
std::vector<unsigned> trim_single_child_nodes(
std::vector<unsigned> tree) {
tree.push_back(0u);
std::vector<bool> mark(tree.size(), false);
mark_nodes(tree, mark);
std::vector<unsigned> ret(1, 0);
tree.pop_back();
mark.pop_back();
auto max_depth = *std::max_element(tree.begin(), tree.end());
std::vector<unsigned> depth_map(max_depth + 1, 0);
for (auto i = 1u; i < tree.size(); ++i) {
if (mark[i]) {
if (tree[i] > tree[i - 1]) {
depth_map[tree[i]] = depth_map[tree[i - 1]];
}
} else {
if (tree[i] > tree[i - 1]) {
depth_map[tree[i]] = depth_map[tree[i - 1]] + 1;
}
ret.push_back(depth_map[tree[i]]);
}
}
return ret;
}
int main() {
std::vector<unsigned> input = {0, 1, 2, 3, 3, 1, 2, 3};
auto output = trim_single_child_nodes(input);
for (auto depth : output) {
std::cout << depth << ' ';
}
}