英文:
Using derived type for recursive tree method
问题
我有一个名为BinaryTree<T>
的类,看起来像这样:
class BinaryTree<T>
where T : IComparable
{
protected BinaryTree<T>? _left;
protected BinaryTree<T>? _right;
protected BinaryTree<T>? _root;
protected T _value;
public T Value
{
get { return _value; }
set { _value = value; }
}
public BinaryTree<T>? Left
{
get { return _left; }
set { _left = value; }
}
public BinaryTree<T>? Right
{
get { return _right; }
set { _right = value; }
}
public BinaryTree<T>? Root
{
get { return _root; }
set { _root = value; }
}
public BinaryTree() { }
public BinaryTree(T value, BinaryTree<T>? left = null, BinaryTree<T>? right = null, BinaryTree<T>? root = null)
{ ... }
public List<T> PreOrder()
{ ... }
public List<T> InOrder()
{ ... }
public List<T> PostOrder()
{ ... }
}
还有一个派生类BinarySearchTree
,它实现了Search
方法:
class BinarySearchTree : BinaryTree<int>
{
public BinarySearchTree() : base() { }
public Boolean Search(int value)
{
if (Value.CompareTo(value) == 0) return true;
if ((Left is not null) && (Value.CompareTo(value) > 0)) { return Left.Search(value); }
else if ((Right is not null) && (Value.CompareTo(value) > 0)) { return Right.Search(value); }
else { return false; }
}
}
Left.Search
(和类似地Right
)不起作用,因为Left
的类型不是BinarySearchTree
,而是BinaryTree<int>
。我知道可以通过在BinaryTree
中覆盖_left
,_right
,Left
和Right
来解决这个问题,但这似乎是多余的,因为新代码在功能上没有任何不同。我也知道我们可以在这种情况下将Search
移动到BinaryTree
中,但这不是重点;对于我只想在派生类中拥有的更复杂方法来说,似乎应该在某个地方提供这种功能。
我最初的想法是尝试将基类中的_left
和_right
的类型设置为派生类的类型。但似乎不可能;我可以使用GetType()
来获取类型的字符串,但无法使用该类型定义属性。
英文:
I have a class BinaryTree<T>
that looks like this:
class BinaryTree<T>
where T : IComparable
{
protected BinaryTree<T>? _left;
protected BinaryTree<T>? _right;
protected BinaryTree<T>? _root;
protected T _value;
public T Value
{
get { return _value; }
set { _value = value; }
}
public BinaryTree<T>? Left
{
get { return _left; }
set { _left = value; }
}
public BinaryTree<T>? Right
{
get { return _right; }
set { _right = value; }
}
public BinaryTree<T>? Root
{
get { return _root; }
set { _root = value; }
}
public BinaryTree() { }
public BinaryTree(T value, BinaryTree<T>? left = null, BinaryTree<T>? right = null, BinaryTree<T>? root = null)
{ ... }
public List<T> PreOrder()
{ ... }
public List<T> InOrder()
{ ... }
public List<T> PostOrder()
{ ... }
}
And a dervied class BinarySearchTree
that implements the Search
method:
class BinarySearchTree : BinaryTree<int>
{
public BinarySearchTree() : base() { }
public Boolean Search(int value)
{
if (Value.CompareTo(value) == 0) return true;
if ((Left is not null) && (Value.CompareTo(value) > 0)) { return Left.Search(value); }
else if ((Right is not null) && (Value.CompareTo(value) > 0)) { return Right.Search(value); }
else { return false; }
}
}
Left.Search
(and analagously Right
) won't work because Left
isn't of type BinarySearchTree
, but rather BinaryTree<int>
. I know that you can solve this by overriding _left
, _right
, Left
and Right
in BinaryTree
, but that seems redundant since the new code isn't doing anything functionally different. I also know that we could just move Search
to BinaryTree
in this case, but that isn't the point; in the case of more complicated methods that I only want to have in a derived class it seems like this kind of functionality should be available somewhere.
My initial idea was to try to set the type of _left
and _right
in the base class to whatever the derived class would be. It doesn't seem like that's possible though; I can get the type as string using GetType()
but I can't define a property with that type.
答案1
得分: 0
首先,你可能应该使用组合而不是继承,即将 BinaryTree<int>
作为 BinarySearchTree
的字段或属性。组合通常使代码更容易使用和理解,因为你不必担心复杂的继承层次结构。
class BinarySearchTree
{
private BinaryTree<int> root;
}
然后,你可以使你的搜索方法接受一个节点作为输入参数,通常使用私有方法进行递归,以及一个使用根节点的公共方法:
class BinarySearchTree
{
private BinaryTree<int> root;
public Boolean Search(int value) => Search(root, value);
private Boolean Search(BinaryTree<int> node, int value)
{
// 进行搜索操作...
}
}
然而,还有一些其他事情需要考虑修复。
递归技巧存在一些问题。如果树平衡不好,可能会导致堆栈溢出。我建议考虑改用迭代技巧。这对于二叉搜索方法来说应该相当简单。
使用 IComparer<T>
参数而不是 IComparable
约束,以及一个重载,如果用户没有明确提供 IComparer<T>
,则使用 Comparer<T>.Default
。这是在灵活性和易用性之间取得良好平衡的做法,也是大多数框架的模式。
使用函数式方法而不是基于类的方法。这通常使代码更容易重用,因为你不会受限于特定的树类型。一个签名可能如下所示:
public static bool BinarySearch<T>(T current, T value, Func<T, (T? Left, T? Right)> selector, IComparer<T> comparer)
如果你处理大型的基本类型树,通常更有用的是使用数组来描述树。对象会带来一些开销,如果你有大量的整数,这些开销会占主导地位。有时,使用数组和索引到数组可以在处理大量数据时更好,典型的示例可能是堆这种情况。
英文:
First of all, you should probably use composition instead of inheritance, i.e. make BinaryTree<int>
a field or property of BinarySearchTree
. Composition tend to make code easier to use and reason about since you don't have to worry about complex inheritance hierarchies.
class BinarySearchTree
{
private BinaryTree<int> root;
}
You can then make your search method take a node as input parameter, commonly using an private method for recursion, and a public method that uses the root:
class BinarySearchTree
{
private BinaryTree<int> root;
public Boolean Search(int value) => Search(root, value);
private Boolean Search(BinaryTree<int> node, int value)
{
...
}
}
However, there are some things other things I would consider fixing.
Recursive techniques have some problems. If the tree is poorly balanced it can lean to stack overflows. I would recommend thinking about using a iterative technique instead. That should be fairly simple for a binary search method.
Use a IComparer<T>
parameter instead of IComparable
restriction, and an overload that uses Comparer<T>.Default
if the user does not specifically give you one. This is a good compromise between flexibility and ease of use, and is the pattern most of the framework uses.
Use a functional approach rather than a class based approach. That tend to make the code easier to reuse, since you are not tied to a specific tree type. A signature might for example look something like this:
public static bool BinarySearch<T>(T current, T value, Func<T, (T? Left, T? Right)> selector, IComparer<T> comparer)
If you are handling large trees of primitive types, it is often more useful to describe the tree using an array. Objects have some overhead, and if you have a very large amount of ints, that overhead will dominiate. Sometimes, using arrays and indexes to that array can be a better solution when dealing with lots of data, a typical example would be something like a heap.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论