【发布时间】:2020-11-06 04:54:06
【问题描述】:
我正在用std::unique_ptr 玩红黑树,但它不起作用。
我的节点定义:
enum class Color {
Red,
Black
};
template <typename T>
struct Node {
T key;
Color color;
std::unique_ptr<Node<T>> left;
std::unique_ptr<Node<T>> right;
Node<T>* parent;
Node(const T& key) : key {key}, parent {nullptr}, color {Color::Red} {}
};
我选择std::unique_ptr,因为std::shared_ptr 很昂贵,并且父母拥有它的左右孩子。简单地说,parent 应该是一个原始指针。
但是,Insert 函数背后的逻辑破坏了我的基本设计:
这是我的树旋转功能。他们接受std::unique_ptr 的右值引用,因为它实际上转移了所有权。
void LeftRotate(std::unique_ptr<Node<T>>&& x) {
auto y = std::move(x->right);
auto yl = y->left.get();
x->right = std::move(y->left);
if (yl) {
yl->parent = x.get();
}
y->parent = x->parent;
auto py = y.get();
if (!x->parent) {
root = std::move(y);
} else if (x == x->parent->left) {
x->parent->left = std::move(y);
} else {
x->parent->right = std::move(y);
}
x->parent = py;
py->left = std::move(x);
}
void RightRotate(std::unique_ptr<Node<T>>&& x) {
auto y = std::move(x->left);
auto yr = y->right.get();
x->left = std::move(y->right);
if (yr) {
yr->parent = x.get();
}
y->parent = x->parent;
auto py = y.get();
if (!x->parent) {
root = std::move(y);
} else if (x == x->parent->left) {
x->parent->left = std::move(y);
} else {
x->parent->right = std::move(y);
}
x->parent = py;
py->right = std::move(x);
}
这是我的Insert 函数:
public:
void Insert(const T& key) {
auto z = std::make_unique<Node<T>>(key);
Insert(std::move(z));
}
private:
void Insert(std::unique_ptr<Node<T>> z) {
Node<T>* y = nullptr;
Node<T>* x = root.get();
while (x) {
y = x;
if (z->key < x->key) {
x = x->left.get();
} else {
x = x->right.get();
}
}
z->parent = y;
if (!y) {
root = std::move(z);
InsertFixup(std::move(root));
} else if (z->key < y->key) {
y->left = std::move(z);
InsertFixup(std::move(y->left));
} else {
y->right = std::move(z);
InsertFixup(std::move(y->right));
}
}
void InsertFixup(std::unique_ptr<Node<T>>&& z) {
auto zp = z->parent;
while (zp && zp->color == Color::Red) {
auto zpp = zp->parent;
if (zp == zpp->left.get()) {
auto y = zpp->right.get();
if (y && y->color == Color::Red) {
zp->color = Color::Black;
y->color = Color::Black;
zpp->color = Color::Red;
zp = zpp->parent;
} else {
if (z == zp->right) {
z = std::unique_ptr<Node<T>>(zp);
auto pz = z.get();
LeftRotate(std::move(z));
zp = pz->parent;
zpp = zp->parent;
}
zp->color = Color::Black;
zpp->color = Color::Red;
auto pzpp = std::unique_ptr<Node<T>>(zpp); // error
RightRotate(std::move(pzpp)); // error
}
} else {
auto y = zpp->left.get();
if (y && y->color == Color::Red) {
zp->color = Color::Black;
y->color = Color::Black;
zpp->color = Color::Red;
zp = zpp->parent;
} else {
if (z == zp->left) {
z = std::unique_ptr<Node<T>>(zp);
auto pz = z.get();
RightRotate(std::move(z));
zp = pz->parent;
zpp = zp->parent;
}
zp->color = Color::Black;
zpp->color = Color::Red;
auto pzpp = std::unique_ptr<Node<T>>(zpp); // error
LeftRotate(std::move(pzpp)); // error
}
}
}
root->color = Color::Black;
}
InsertFixup 中的以下几行是错误的:
auto pzpp = std::unique_ptr<Node<T>>(zpp); // error
LeftRotate(std::move(pzpp)); // error
我想做的是围绕节点z的奶奶旋转树。。
但是,问题是无法获取拥有奶奶节点的std::unique_ptr(需要传递给LeftRotate函数),因为我的节点的父链接实现给出了一个原始指针。当然,我可以从根部往下追,但这样做会破坏RB-Tree插入操作的对数时间复杂度,使其无用。
我应该改用std::shared_ptr 吗?有没有办法用std::unique_ptr实现RB-tree?
【问题讨论】:
-
我怀疑
shared_ptr的额外费用会显示在个人资料中。但它可能会大大简化代码。另外,家长可以考虑weak_ptr。 -
@Jeffrey 是的,如果我选择将
std::shared_ptr用于left, right,那么parent应该是std::weak_ptr。实际上,我已经使用std::shared_ptr实现过一次。但有人告诉我,C++ 社区不鼓励使用std::shared_ptr,除非指针以不确定的顺序在多个线程之间共享。 -
你不能沿着你的原始指针向上走,直到你点击拥有感兴趣节点的节点吗?即实际持有
std::unique_ptr的节点 -
Yes
unique_ptr是更好的选择,因为数据结构是数据的明确所有者。它基本上是一个实现细节。有明确的层级和责任,没有共享内部数据,因此shared_ptr在这里是错误的 -
@frozenca 不,那不是真的。假设
zpp是左孩子:std::unique_ptr<Node> zpp = std::move(zp->parent->parent->left)。在实际代码中,您必须检查它是哪一个。
标签: c++ c++11 smart-pointers