订单统计树的变体将允许您按 O(log n) 中的索引添加和获取。
基本思路如下:
- 让每个节点存储以该节点为根的子树的大小。
节点的索引将对应于它在树的有序遍历中的位置。
这意味着节点的顺序是根据它们在树中出现的位置确定的——这不是二叉搜索树通常的工作方式,其中节点的元素具有一些不依赖于它出现在树中的位置的顺序( egf
大于a
按字典顺序排序的常规 BST,但在我们的例子f
中可能小于或大于a
,因为它是根据 和 的索引排序的。f
a
要添加或获取,我们从根开始并递归地遍历树,根据目标索引和子树大小确定我们的插入或查找位置是在左侧还是右侧。
更具体地说,我们有以下递归定义:(
对于空节点和实际插入节点增加了一些复杂性)
node.add(index, element):
if index <= left.subtreeSize
left.add(index, element)
else
// anything to the right is after left subtree and current node, so those must be excluded
right.add(index - left.subtreeSize - 1, element)
node.get(index, element):
if index == left.subtreeSize
return node
if index < left.subtreeSize
return left.get(index)
else
return right.get(index - left.subtreeSize - 1)
为了更好地理解这一点,以下示例树可能会有所帮助:
Values: Indices (in-order pos): Subtree sizes:
a 5 8
/ \ / \ / \
b g 1 6 5 2
/ \ \ / \ \ / \ \
f c h 0 3 7 1 3 1
/ \ / \ / \
e d 2 4 1 1
例如,如果我们想在位置 5 插入一个新节点,它将被插入到d
.
下面是一个小测试程序来演示这一点(创建上面显示的树)。
请注意,仍然需要进行平衡以实现每个操作的 O(log n) 运行时间。
class Test
{
static class Node<T>
{
Node<T> left, right;
T data;
int subtreeCount;
Node(T data) { this.data = data; subtreeCount = 1; }
public String toString(int spaces, char leftRight)
{
return String.format("%" + spaces + "s%c: %s\n", "", leftRight, data.toString())
+ (left != null ? left.toString(spaces+3, 'L') : "")
+ (right != null ? right.toString(spaces+3, 'R') : "");
}
int subtreeSize(Node<T> node)
{
if (node == null)
return 0;
return node.subtreeCount;
}
// combined add and get into 1 function for simplicity
// if data is null, it is an get, otherwise it's an add
private T addGet(int index, T data)
{
if (data != null)
subtreeCount++;
if (index == subtreeSize(left) && data == null)
return this.data;
if (index <= subtreeSize(left))
{
if (left == null && data != null)
return (left = new Node<>(data)).data;
else
return left.addGet(index, data);
}
else if (right == null && data != null)
return (right = new Node<>(data)).data;
else
return right.addGet(index-subtreeSize(left)-1, data);
}
}
static class TreeArray<T>
{
private Node<T> root;
public int size() { return (root == null ? 0 : root.subtreeCount); }
void add(int index, T data)
{
if (index < 0 || index > size())
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size());
if (root == null)
root = new Node<>(data);
else
root.addGet(index, data);
}
T get(int index)
{
if (index < 0 || index >= size())
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size());
return root.addGet(index, null);
}
@Override
public String toString() { return root == null ? "Empty" : root.toString(1, 'X'); }
}
public static void main(String[] args)
{
TreeArray<String> tree = new TreeArray<>();
tree.add(0, "a");
tree.add(0, "b");
tree.add(1, "c");
tree.add(2, "d");
tree.add(1, "e");
tree.add(0, "f");
tree.add(6, "g");
tree.add(7, "h");
System.out.println("Tree view:");
System.out.print(tree);
System.out.println("Elements in order:");
for (int i = 0; i < tree.size(); i++)
System.out.println(i + ": " + tree.get(i));
}
}
这输出:
Tree view:
X: a
L: b
L: f
R: c
L: e
R: d
R: g
R: h
Elements in order:
0: f
1: b
2: e
3: c
4: d
5: a
6: g
7: h
现场演示。