我创建了一个二叉搜索树。我需要实现一个索引器,它可以帮助我获取树的元素,但它必须是排序序列的一部分。当然,我可以做类似
的事情public T this[int i]
{
get
{
var list = this.ToList();
return list[i];
}
}
因为我已经实现了公共IEnumerator<T> GetEnumerator()
,它获得了排序顺序,但如果我有很多像
for(int i = 0; i < 10000000; i++)
{
tree.Add(i);
Console.Write(tree[i]);
}
我想优化此任务。怎么样? 完整代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace BinaryTrees
{
public class BinaryTree<T> : IEnumerable<T>
where T : IComparable
{
private Node root;
public int Count { get; private set; }
public class Node
{
public Node left, right;
public T value;
}
public BinaryTree()
{
root = new Node();
}
public T this[int i]
{
get
{
var list = this.ToList();
//How to
//make it fast??
return list[i];
}
}
public void Add(T key)
{
var current = root;
while (true)
{
if (Count == 0)
{
current.value = key;
Count++;
break;
}
if (key.CompareTo(current.value) <= 0)
{
if (current.left == null)
{
current.left = new Node { value = key };
Count++;
break;
}
else
current = current.left;
}
if (key.CompareTo(current.value) > 0)
{
if (current.right == null)
{
current.right = new Node { value = key };
Count++;
break;
}
else
current = current.right;
}
}
}
public bool Contains(T key)
{
var current = root;
while (true)
{
if (Count == 0)
return false;
if (current == null)
return false;
var result = key.CompareTo(current.value);
if (result == 0)
return true;
if (result < 0)
{
current = current.left;
}
else if (result > 0)
{
current = current.right;
}
}
}
public IEnumerator<T> GetEnumerator()
{
var stack = new Stack<Node>();
var current = root;
var done = false;
while (!done)
{
if (current != null)
{
stack.Push(current);
current = current.left;
}
else if (stack.Count != 0)
{
current = stack.Pop();
yield return current.value;
current = current.right;
}
else done = true;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
我认为,也许我可以创建一个数组,只要我添加了一个元素,我就可以在索引器中对它进行排序,但我意识到这也是一个坏主意。
答案 0 :(得分:1)
最简单的解决方案是尽可能避免索引。如果您已经拥有枚举器,并且需要索引,请手动保留它,并且不要编制索引。如果插入后需要节点,请从add方法返回:
int index =0;
foreach (var item in tree)
{
Console.WriteLine($"At {index} - {item}");
index++;
}
如果无法做到这一点,优化索引访问需要使用额外的内存来跟踪树的每个分支上有多少个节点并使用此信息进行索引。这有几个副作用:
它将Node
对象的大小增加Count
字段的字节数(例如int
- 例如4个字节),这可能不是很简单< / p>
这会增加复杂性,以便在Add
和Remove
它将树可以容纳的最大元素数限制为用于计数的数据类型的大小。
如果你对这些权衡做得好,你可以这样做:你在每个节点中保留该子树中元素的数量。如果索引大于左侧树中的计数,则索引位于右侧子树中,如果索引较低,则索引位于左侧树中,如果索引等于左子树中的计数,则表示搜索索引是当前元素。如果我们继续使用正确的树,我们需要将索引调整为相对于右子树的索引。
public class BinaryTree<T> : IEnumerable<T>
where T : IComparable
{
private Node root;
public int Count { get; private set; }
public class Node
{
public int count;
public Node left, right;
public T value;
}
public BinaryTree()
{
root = new Node();
}
public T this[int i]
{
get
{
var current = this.root;
while (true)
{
int beforeCount = current.left?.count ?? 0;
if (beforeCount == i ) return current.value;
if (beforeCount < i)
{
i = i - beforeCount - 1;
current = current.right;
}
else
{
current = current.left;
}
}
}
}
public void Add(T key)
{
var current = root;
if (Count == 0)
{
current.value = key;
current.count = 1;
Count++;
return;
}
while (true)
{
current.count++;
if (key.CompareTo(current.value) <= 0)
{
if (current.left == null)
{
current.left = new Node { value = key, count = 1 };
Count++;
break;
}
else
{
current = current.left;
}
}
else if (key.CompareTo(current.value) > 0)
{
if (current.right == null)
{
current.right = new Node { value = key, count = 1 };
Count++;
break;
}
else
{
current = current.right;
}
}
}
}
}