【问题标题】:Augmenting data structure without wasting memory在不浪费内存的情况下扩充数据结构
【发布时间】:2015-02-13 05:09:42
【问题描述】:

我有一个类Tree,我想将其扩充为更专业的数据结构,例如Order_treeInterval_tree。这些扩充需要对Node 进行补充,例如尺寸信息,以及对某些算法的微小改动。

我想知道在性能、可读性和可维护性方面在 C++ 中实现增强的最佳方法。树不应该以多态方式使用。到目前为止,我尝试的是公开继承Tree,然后重载基本方法。 (我很抱歉成为面向对象编程的初学者)

template <typename T>
class Tree {
protected:
    enum class Color : char {BLACK = 0, RED = 1};

    struct Node {
        T key;
        Node *parent, *left, *right;
        Color color;
        Node() : color{Color::BLACK} {} // sentinel construction
        Node(T val, Color col = Color::RED) : key{val}, parent{nil}, left{nil}, right{nil}, color{col} {}
    };
    using NP = typename Tree::Node*;

    NP root {nil};
    // nil sentinel
    static NP nil;

    // core utility algorithms...

};

template <typename T>
typename Tree<T>::NP Tree<T>::nil {new Node{}};

订单树

template <typename T>
class Order_tree : public Tree<T> {
    using Color = typename Tree<T>::Color;
    using Tree<T>::Tree;    // inherit constructors
    struct Order_node {
        T key;
        Order_node *parent, *left, *right;
        size_t size;    // # of descendent nodes including itself = left->size + right->size + 1
        Color color;
        Order_node() : size{0}, color{Color::BLACK} {}  // sentinel construction
        Order_node(T val, Color col = Color::RED) : key{val}, parent{nil}, left{nil}, right{nil}, size{1}, color{col} {}
    };
    using NP = typename Order_tree::Order_node*;
    NP root {nil};
    static NP nil;

    // overloading on only the methods that need changing
};

template <typename T>
typename Order_tree<T>::NP Order_tree<T>::nil {new Order_node{}};

但是,这不能正常运行,因为现在我有 2 个根和 2 个 nil,所有基本方法都在基本根上工作,并且使用 Tree&lt;T&gt;::NP 而不是 Order_tree::NP 所以 Order_node 的大小属性不能使用。

一种方法是复制粘贴代码,这是非常难以维护的。我认为另一种方法是在 T 和 NP 上模板 Tree,以便 Order_tree 是别名 using Order_tree = Tree&lt;Order_node&gt; 并在节点上专门化树。

【问题讨论】:

  • 将各种tree 类的T 模板参数作为它们应该存储在每个节点中的结构更有意义,包括您当前正在考虑的value 和“增强”数据。如果您希望T 数据类型的特定部分充当键,您可以要求将该字段称为key,对于key_type; 有一个typedef 或任何其他支持您的预期客户端使用的东西;或者,您可以为密钥设置第二个模板参数,就像 std::map 所做的那样。
  • 这会是一个额外的间接级别吗(在可读性和性能方面)? (要访问密钥,您必须说 node-&gt;data-&gt;key 假设节点的有效负载是 T data 其中您建议的数据将是一个至少包含一个密钥的结构。

标签: c++ templates inheritance binary-search-tree


【解决方案1】:

如果您真的对“所有树的通用树”感兴趣,那么问题似乎不在树上,而在 Node.js 上。您需要一些特殊的节点情况,那么为什么不将它们也泛化呢?例如:

 template <typename T>
class Tree {
protected:
    struct BaseNode {
    //all code you really can generalize here 
    };

    struct Node : public BaseNode {
    //You need Node here only if you want your base Tree class to be ready to use.
    //If you want to use only its derives such as Order_tree,
    //you create special nodes kinds only there
    };

    // core utility algorithms...

BaseNode * root; //Only one root node, there is no need in duplication! 
                 //You can instantiate it as root = new OrderTreeNode or root = new SpecialTreeNode in any derives.

};

然而 Node 虚函数调用的代价是相当大的。所以你需要清楚地理解——你需要泛化而不是重复代码还是你需要性能。

【讨论】:

  • 我明白了,所以这里没有妥协?我认为我要求的功能是合理的 - 静态绑定 Tree 类的所有方法以处理其他类型的 Nodes,并覆盖我明确更改的那些。
  • 你可以通过一些模板特征来做到这一点,但是代码会很大而且重复很多。这一点是我个人要从 C++ 切换到 Rust 的主要原因,后者在语言本身具有清晰简单的 trait 机制。
【解决方案2】:

经过一些实验,我找到了实现我想要的最佳方法:

  • 节点类型上的模板树
  • 使 nil 成为每个 Node 类型的静态元素
  • 移动一些在节点上工作的私有方法,而不依赖于 根除为在 Node 上模板化的普通函数
  • 将可能更改的功能设为虚拟
  • 公开增加树 继承它并覆盖必要的虚函数
  • 使用 base Tree 的根(在派生类中不保存数据)

现在的样子:
tree.h

namespace sal {

// utilities with no dependence on root, outside of class now
template <typename Node>
Node* tree_find(Node* start, typename Node::key_type key) {
    while (start != Node::nil && start->key != key) {
        if (key < start->key) start = start->left;
        else start = start->right;
    }
    return start;
}
// more of them...

template <typename Node>
class Tree {
protected:
    using NP = Node*;
    using T = typename Node::key_type;

    // nil is static member of each Node type now
    NP root {Node::nil};

    // virtual methods that could be changed by augmentation
    virtual void rotate_left(NP node);
    virtual void rotate_right(NP node);
    virtual void tree_insert(NP start, NP node);
    virtual void rb_delete(NP node);

    // non-virtual methods that are never overridden
    void rb_insert_fixup(NP node);
    void rb_delete_fixup(NP successor);
    void rb_insert(NP node);  // just a call to tree_insert and rb_insert_fixup
    void transplant(NP old, NP moved);
public:
    virtual ~Tree();  // does all the clean up so its derived classes don't have to
    // interface...
};

template <typename T>
struct Basic_node {
    static Basic_node* nil;

    using key_type = T;
    T key;
    Basic_node *parent, *left, *right;
    Color color;
    Basic_node() : color{Color::BLACK} {}   // sentinel construction
    Basic_node(T val) : key{val}, parent{nil}, left{nil}, right{nil}, color{Color::RED} {}
};

template <typename T>
using Basic_tree = Tree<Basic_node<T>>;

template <typename T>
Basic_node<T>* Basic_node<T>::nil {new Basic_node{}};

}

order_tree.h

#include "tree.h"

namespace sal {

template <typename Node>
class Order_augment : public Tree<Node> {

    using NP = Node*;
    using T = typename Node::key_type;
    using Tree<Node>::root;

    // no need to redefine shared core functions
    using Tree<Node>::rb_insert;
    using Tree<Node>::transplant;
    using Tree<Node>::rb_insert_fixup;
    using Tree<Node>::rb_delete_fixup;

    // order statistics operations
    NP os_select(NP start, size_t rank) const;
    size_t os_rank(NP node) const;

    // modification of rb operations to maintain augmentation
    virtual void tree_insert(NP start, NP node) override;
    virtual void rb_delete(NP node) override;
    virtual void rotate_left(NP node) override;
    virtual void rotate_right(NP node) override;
public:
    // augmented interface
};

template <typename T>
struct Order_node {
    static Order_node* nil;

    using key_type = T;
    T key;
    Order_node *parent, *left, *right;
    size_t size;    // # of descendent nodes including itself = left->size + right->size + 1
    Color color;
    Order_node() : size{0}, color{Color::BLACK} {}  // sentinel construction
    Order_node(T val) : key{val}, parent{nil}, left{nil}, right{nil}, size{1}, color{Color::RED} {}
};

template <typename T>
Order_node<T>* Order_node<T>::nil {new Order_node{}};

template <typename T>
using Order_tree = Order_augment<Order_node<T>>;

}

结果是保存增强数据结构的文件大小现在大约是原来的 1/3,并且完全删除了代码重复!这意味着任何改进核心方法的更改都可以仅本地化到 tree.h,并且所有增强树也将感受到它的效果。

【讨论】:

    猜你喜欢
    • 2018-02-17
    • 1970-01-01
    • 2013-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-11
    • 2011-06-02
    相关资源
    最近更新 更多