执行范围最小查询

执行 RMQ 的过程已在介绍中显示。用于检查范围最小查询的伪代码将是:

Procedure RangeMinimumQuery(SegmentTree, qLow, qHigh, low, high, position):
if qLow <= low and qHigh >= high        //Total Overlap
    Return SegmentTree[position]
else if qLow > high || qHigh < low      //No Overlap
    Return infinity
else                                    //Partial Overlap
    mid := (low+high)/2
    Return min(RangeMinimumQuery(SegmentTree, qLow, qHigh, low, mid, 2*position+1),
               RangeMinimumQuery(SegmentTree, qLow, qHigh, mid+1, high, 2*position+2))
end if

在这里,qLow = The lower range of our queryqHigh = The upper range of our querylow = starting index of Item arrayhigh = Finishing index of Item arrayposition = root = 0。现在让我们尝试使用我们之前创建的示例来理解该过程:

StackOverflow 文档

我们的 SegmentTree 数组:

   0     1     2     3     4     5     6
+-----+-----+-----+-----+-----+-----+-----+
|  -1 |  -1 |  0  |  -1 |  2  |  4  |  0  |
+-----+-----+-----+-----+-----+-----+-----+

我们希望找到范围内的最小值 [1,3]
由于这是一个递归过程,我们将看到 RangeMinimumQuery 的操作使用递归表来跟踪 qLowqHighlowhighpositionmidcalling line。首先,我们调用 RangeMinimumQuery(SegmentTree,1,3,0,3,0 。这里,前两个条件不满足(部分重叠)。我们将得到一个 mid.calling line 表示在此声明后调用了 RangeMinimumQuery 我们将程序中的 RangeMinimumQuery 调用分别表示为 1 和 **2。**我们的表格如下所示:

+------+-------+-----+------+----------+-----+--------------+
| `qLow` | qHigh | low | high | position | mid | calling line |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   3  |     0    |  1  |       1      |
+------+-------+-----+------+----------+-----+--------------+

所以当我们调用 RangeMinimumQuery-1 时,我们通过:low = 0high = mid = 1position = 2*position+1 = 1。有一件事你可以注意到,那就是 2*position+1节点的左子节点。这意味着我们正在检查 root 的左子。由于 [1,3] 部分重叠 [0,1] ,前两个条件不满足,我们得到一个 mid。我们的表:

+------+-------+-----+------+----------+-----+--------------+
| `qLow` | qHigh | low | high | position | mid | calling line |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   3  |     0    |  1  |       1      |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   1  |     1    |  0  |       1      |
+------+-------+-----+------+----------+-----+--------------+

在下一个递归调用中,我们通过 low = 0high = mid = 0position = 2*position+1 = 3。我们到达树的最左边的叶子。由于 [1,3] 不与 [0,0] 重叠,我们将 infinity 返回给我们的调用函数。递归表:

+------+-------+-----+------+----------+-----+--------------+
| `qLow` | qHigh | low | high | position | mid | calling line |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   3  |     0    |  1  |       1      |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   1  |     1    |  0  |       1      |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   0  |     3    |     |              |
+------+-------+-----+------+----------+-----+--------------+

由于此递归调用已完成,我们将返回到递归表的上一行。我们得到:

+------+-------+-----+------+----------+-----+--------------+
| `qLow` | qHigh | low | high | position | mid | calling line |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   3  |     0    |  1  |       1      |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   1  |     1    |  0  |       1      |
+------+-------+-----+------+----------+-----+--------------+

在我们的程序中,我们执行 RangeMinimumQuery-2 调用。这一次,我们通过 low = mid+1 = 1high = 1position = 2*position+2 = 4。我们的 calling line changes to **2**。我们得到:

+------+-------+-----+------+----------+-----+--------------+
| `qLow` | qHigh | low | high | position | mid | calling line |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   3  |     0    |  1  |       1      |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   1  |     1    |  0  |       2      |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  1  |   1  |     4    |     |              |
+------+-------+-----+------+----------+-----+--------------+

所以我们要去上一个节点的正确孩子。这次总重叠。我们返回值 SegmentTree[position] = SegmentTree[4] = 2

+------+-------+-----+------+----------+-----+--------------+
| `qLow` | qHigh | low | high | position | mid | calling line |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   3  |     0    |  1  |       1      |
+------+-------+-----+------+----------+-----+--------------+

回到调用函数,我们检查两个调用函数的两个返回值的最小值。显然最小值为 2 ,我们将 2 返回给调用函数。我们的递归表看起来像:

+------+-------+-----+------+----------+-----+--------------+
| `qLow` | qHigh | low | high | position | mid | calling line |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   3  |     0    |  1  |       1      |
+------+-------+-----+------+----------+-----+--------------+

我们已经完成了查看分段树的左侧部分。现在我们从这里调用 RangeMinimumQuery-2。我们将通过 low = mid+1 = 1+1 =2high = 3position = 2*position+2 = 2。我们的表:

+------+-------+-----+------+----------+-----+--------------+
| `qLow` | qHigh | low | high | position | mid | calling line |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  0  |   3  |     0    |  1  |       1      |
+------+-------+-----+------+----------+-----+--------------+
|   1  |   3   |  2  |   3  |     2    |     |              |
+------+-------+-----+------+----------+-----+--------------+

总重叠。所以我们返回值:SegmentTree[position] = SegmentTree[2] = 0。我们回到调用这两个子节点的递归,得到最小值 (4,0) ,即 0 并返回值。

执行该过程后,我们得到 0 ,这是从 index- 1 到 index- 3 的最小值。

此过程的运行时复杂性为 O(logn),其中 nItems 数组中的元素数。要执行范围最大查询,我们需要替换该行:

Return min(RangeMinimumQuery(SegmentTree, qLow, qHigh, low, mid, 2*position+1),
               RangeMinimumQuery(SegmentTree, qLow, qHigh, mid+1, high, 2*position+2))

有:

Return max(RangeMinimumQuery(SegmentTree, qLow, qHigh, low, mid, 2*position+1),
               RangeMinimumQuery(SegmentTree, qLow, qHigh, mid+1, high, 2*position+2))