Java 序列中两项之间的最小值

Java 序列中两项之间的最小值,java,algorithm,binary-tree,complexity-theory,Java,Algorithm,Binary Tree,Complexity Theory,我正在尝试制作一个程序,它将在O(N)时间内将一系列数字保存在一个数组中,以便快速(O(logn))回答以下问题 min(inti,intj):返回最小值在position i和j之间的序列中的position e、 如果序列是A=(22,51,83,42,90,102,114,35),我调用min(3,6) 它将返回4,因为42

我正在尝试制作一个程序,它将在O(N)时间内将一系列数字保存在一个数组中,以便快速(O(logn))回答以下问题

min(inti,intj):返回最小值在position i和j之间的序列中的position

e、 如果序列是A=(22,51,83,42,90,102,114,35),我调用min(3,6) 它将返回4,因为42<83,90102

我知道如果序列的值没有排序,就不可能实现快速时间,因为我想实现O(logn),所以我想到了实现一个二叉树


问题是,我无法确定应该以何种方式将序列的值放置在二叉树中以快速访问它们,以使min()能够根据需要工作

这是一个需要用区间树解决的典型问题。您可以在
O(n)
time中构造它,然后在
O(logn)
中运行查询

一般的想法是将一个完美的二叉树存储在一个数组中,其中索引
i
处的节点的子节点位于索引
2i
2i+1
处。在叶中存储序列的值,对于每个非叶节点,存储其所有子节点的最小值。如果从叶子向上构建树,可以在
O(n)
时间内完成

要运行间隔查询,可以采用两种基本方法(都在
O(logn)
时间内工作):

  • 从叶子
    a
    b
  • 递归地从树的根开始向下移动
这两种方法的描述可以在互联网上的“间隔树”短语下轻松找到。对于你的问题,我绝对推荐前一个,因为它应该快一点

根据要求,我扩展了我的答案,并给出了查询树的说明。让我们仔细看看我为您的问题建议的自下而上的方法。我假设数组的索引从
0
n-1
。我还假设对于某些自然的
k
,n等于
2^k
。如果不是,则在查询最小值的情况下,通过在底层末尾添加
+Inf
元素将其增加到最接近的2次方。它不会影响任何有效的查询,并且您可以得到一个完美的二叉树,正如我前面描述的那样,它可以很容易地索引。对于一个舒适的实现,我建议对根使用索引1,这也是本描述的假设

这幅画应该把事情弄清楚。底部的黑色索引是来自原始数组的索引。每个节点旁边的绿色索引是树中的索引。现在忽略矩形,因为它们与查询示例有关

通过
query(a,b)
我们将表示区间
[a;b]
中最小值的查询。首先,一个特例是:当
a
等于
b
时,我们只返回
tree[n+a]
(请注意,当
tree[1]
是根时,这是正确的索引)

让我们转到更复杂的情况,当
a!=b
。该算法的线索是,我们可以将任何区间分割成
O(logn)
基本区间,这些区间没有公共元素,完全覆盖原始区间。每个基本间隔的大小是2的幂,每个基本间隔由我们的一个节点表示。当我们列出所有相关的时间间隔时,我们只需要取它们的最小节点就可以得到
查询(a,b)
的答案

现在我们将描述选择基准区间的方法。在示例图像中,它们都被矩形包围。请查看以下代码段:

int x = a + n;
int y = b + n;
int result = Math.min(tree[x], tree[y]);

while (x / 2 != y / 2) {
    if (x % 2 == 0) {
      result = Math.min(result, tree[x + 1]);
    }
    if (y % 2 == 1) {
      result = Math.min(result, tree[y - 1]);
    }

    x /= 2;
    y /= 2;
}
首先,我们将原始索引转换为树中的索引。然后我们考虑包含查询边界的单个项间隔。记住,我已经排除了当
a==b
时的特殊情况

该算法如下进行,向上移动树。无论何时
x%2==0
我们都会考虑树中
x
的同级间隔。请检查此同级始终完全包含在间隔
[a;b]
中。对于
y%2==1
我们也会这样做,但兄弟姐妹在
y
的左侧除外。当
x/2==y/2
时,意味着
x
y
现在是兄弟,我们应该停止该算法。您可以自己检查,这种方法选择的间隔是否完全覆盖了
[a;b]

请注意,我们最多可以检查树底部级别的4个节点。在每个级别上,我们将检查不超过2个节点。由于树中有
O(logn)
级别,我们可以看到任何查询的时间复杂度都是
O(logn)


奖励-修改阵列。您描述的问题不需要修改数组,但在基本情况下,它非常干净,因此我将在这里添加它。如果您还想处理
set(a,v)
指令,这意味着
array[a]=v
您可以在
O(logn)
时间内轻松完成。首先设置
tree[a+n]=v
,然后向根方向移动,更新路径上的最小值。

这是使用间隔树解决的典型问题。您可以在
O(n)
time中构造它,然后在
O(logn)
中运行查询

一般的想法是将一个完美的二叉树存储在一个数组中,其中索引
i
处的节点的子节点位于索引
2i
2i+1
处。在叶中存储序列的值,对于每个非叶节点,存储其所有子节点的最小值。如果从叶子向上构建树,可以在
O(n)