我正在编写一个不可变的二叉树类,其中所有方法(Insert,Remove,RotateLeft等)都返回树的新实例,而不是在适当的位置修改它。
我将创建许多不同的树实现:Avl树,红黑树,splay树等。我有以下内容:
public class AbstractBinaryTree<TreeType, T>
where TreeType : AbstractBinaryTree<TreeType, T>
where T : IComparable<T>
{
protected abstract TreeType CreateNode(TreeType left, T value, TreeType right);
protected abstract T Value { get; }
protected abstract TreeType Left { get; }
protected abstract TreeType Right { get; }
protected abstract bool IsNil();
public TreeType Insert(T item)
{
if (this.IsNil())
{
return CreateNode(this, item, this);
// ^ doesn't compile, can't convert type
// AbstractBinaryTree<TreeType, T> to type TreeType
}
else
{
int compare = item.CompareTo(this.Value);
if (compare < 0)
{
return CreateNode(this.Left.Insert(item), this.Value, this.Right);
}
else if (compare > 0)
{
return CreateNode(this.Left, this.Value, this.Right.Insert(Value));
}
else
{
return this;
// ^ doesn't compile, can't converrt type
// AbstractBinaryTree<TreeType, T> to type TreeType
}
}
}
}
这里的想法是AbstractBinaryTree是一个树节点 - 不仅如此,它与TreeType
的类型相同。如果我可以使上面的基类正常工作,那么我可以这样写:
public class AvlTree<T> : AbstractBinaryTree<AvlTree<T>, T>
{
public override AvlTree<T> Insert(T item) { return Balance(base.Insert(item)); }
}
以便我的Insert方法返回AvlTree<T>
而不是AbstractBinaryTree<AvlTree<T>, T>
。但是我甚至无法做到这一点,因为基类不能编译。
如何将AbstractBinaryTree实例传递给采用TreeType
类型的方法?
答案 0 :(得分:2)
我真的没有答案 - 只是一些可能有用的提示。我认为这可以使用一种名为 self types 的概念的语言(我无法找到并且链接好的网站!)。无论如何,自我类型意味着您可以声明一个抽象基类(比如A
),并且它可以有一个返回自我类型的方法。在创建一个继承的类(比如B
)时, self type 的使用将引用B
(这很有趣,因为基类不知道这个类)。对于C#4粉丝,自我类型是协变的。
无论如何,您可以尝试使用泛型搜索在C#中模拟自我类型的方法...
另一个指针是我前段时间看过的一篇文章。据我所知,它以与你类似的方式使用泛型,所以也许它可以给你一些提示如何解决问题。
答案 1 :(得分:2)
使用AbstractBinaryTree<TreeType, T>
public abstract class AbstractBinaryTree<TreeType, T>
where TreeType : AbstractBinaryTree<TreeType, T>
where T : IComparable<T>
{
protected abstract TreeType CreateNode(AbstractBinaryTree<TreeType, T> left, T value, AbstractBinaryTree<TreeType, T> right);
protected abstract T Value { get; }
protected abstract TreeType Left { get; }
protected abstract TreeType Right { get; }
protected abstract bool IsNil();
public virtual AbstractBinaryTree<TreeType, T> Insert(T item)
{
if (this.IsNil())
{
return CreateNode(this.Left, item, this.Right);
// ^ doesn't compile, can't convert type
// AbstractBinaryTree<TreeType, T> to type TreeType
}
else
{
int compare = item.CompareTo(this.Value);
if (compare < 0)
{
return CreateNode(this.Left.Insert(item), this.Value, this.Right);
}
else if (compare > 0)
{
return CreateNode(this.Left, this.Value, this.Right.Insert(Value));
}
else
{
return this;
// ^ doesn't compile, can't converrt type
// AbstractBinaryTree<TreeType, T> to type TreeType
}
}
}
}
public class AvlTree<T> : AbstractBinaryTree<AvlTree<T>, T>
where T : IComparable<T>
{
public override AbstractBinaryTree<AvlTree<T>, T> Insert(T item)
{
return base.Insert(item);
}
}
使用Balance()进行投射
private AvlTree<T> Balance(AbstractBinaryTree<AvlTree<T>, T> item)
{
return (AvlTree<T>)item;
}
public override AbstractBinaryTree<AvlTree<T>, T> Insert(T item)
{
return Balance(Insert(item));
}
答案 2 :(得分:2)
AbstractBinaryTree
已包含Left,Value和Right属性,因此我可以使用CreateNode(this.Left, this.Value, this.Right)
创建当前节点的副本,而不是尝试返回this
:
public abstract class AbstractBinaryTree<TreeType, T>
where TreeType : AbstractBinaryTree<TreeType, T>
where T : IComparable<T>
{
protected abstract TreeType CreateNil();
protected abstract TreeType CreateNode(TreeType left, T value, TreeType right);
protected abstract T Value { get; }
protected abstract TreeType Left { get; }
protected abstract TreeType Right { get; }
protected abstract bool IsNil();
public virtual TreeType Insert(T item)
{
if (this.IsNil())
{
// can't return 'this', so just creating a new nil node
TreeType nil = CreateNil();
return CreateNode(nil, item, nil);
}
else
{
int compare = item.CompareTo(this.Value);
if (compare < 0)
{
return CreateNode(this.Left.Insert(item), this.Value, this.Right);
}
else if (compare > 0)
{
return CreateNode(this.Left, this.Value, this.Right.Insert(Value));
}
else
{
// can't return 'this', so just creating a new node with a
// copy of the same values
return CreateNode(this.Left, this.Value, this.Right);
}
}
}
}
public class AvlTree<T> : AbstractBinaryTree<AvlTree<T>, T>
{
public override AvlTree<T> Insert(T value) { return Balance(base.Insert(value)); }
}
AvlTree的实现效果非常好,因为我们在向下的路上递归插入树中,并在callstack展开时平衡树。
如果有人可以建议让我重用this
而不是使用其值的副本分配新对象的方式,我想听听它,但是现在这似乎有效。