📜  范围最小查询(平方根分解和稀疏表)(1)

📅  最后修改于: 2023-12-03 14:57:12.012000             🧑  作者: Mango

范围最小查询(平方根分解和稀疏表)

在计算机科学中,范围最小查询是一个常见的问题,用于在数列中找到一段区间内的最小元素。平方根分解和稀疏表是两种常见且高效的解决方法。

平方根分解

平方根分解的思想是将原数据分块,每个块的大小为 $\sqrt{n}$,其中 $n$ 是数列的大小。对于每个块,我们可以预处理出该块内的最小元素,并将这些值存储在一个辅助数组中。

查询时,我们可以确定查询区间内的每个块,然后在辅助数组中所在块的最小元素中找到最小值。接着,我们可以遍历相应的块,以查找该块中的最小元素,并返回区间内所有块中的最小元素。

平方根分解的时间复杂度为 $O(\sqrt{n})$,空间复杂度也为 $O(\sqrt{n})$。

以下是平方根分解的 Python 代码实现:

import math

class SquareRootDecomposition:
    def __init__(self, arr):
        block_size = math.isqrt(len(arr))
        self.blocks = [float("inf")] * (len(arr) // block_size + 1)
        self.min_in_block = [float("inf")] * len(arr)
        
        for i in range(len(arr)):
            self.min_in_block[i] = arr[i]
            if self.blocks[i // block_size] > arr[i]:
                self.blocks[i // block_size] = arr[i]
    
    def query(self, l, r):
        block_size = int(math.isqrt(len(self.min_in_block)))
        left_block = l // block_size
        right_block = r // block_size
        
        ans = float("inf")
        for i in range(l, min((left_block + 1) * block_size, r + 1)):
            ans = min(ans, self.min_in_block[i])
        for i in range(left_block + 1, right_block):
            ans = min(ans, self.blocks[i])
        for i in range(right_block * block_size, r + 1):
            ans = min(ans, self.min_in_block[i])
        
        return ans
稀疏表

稀疏表的思想是将原数据拆分成一系列累加和数组,并将这些数组保存在一个二维表格中。每个格子 $(i, j)$ 中存储的是从位置 $i$ 开始,长度为 $2^j$ 的子段的最小值。

我们可以通过对表格预处理来回答查询。对于任何子段 $[l, r]$,我们可以在 $O(1)$ 时间内找到两个相邻的长度为 $2^j$ 的子段,其中 $l$ 在第一个子段中,$r$ 在第二个子段中。查询的答案即是这两个子段的最小值。

稀疏表的时间复杂度为 $O(n \log{n})$,空间复杂度为 $O(n \log{n})$。

以下是稀疏表的 Python 代码实现:

class SparseTable:
    def __init__(self, arr):
        n = len(arr)
        log_n = int(math.ceil(math.log2(n)))
        self.st = [[float("inf")] * (log_n + 1) for _ in range(n)]
        
        for i in range(n):
            self.st[i][0] = arr[i]
        
        for j in range(1, log_n + 1):
            for i in range(n):
                if i + (1 << j) > n:
                    break
                self.st[i][j] = min(self.st[i][j - 1], self.st[i + (1 << (j - 1))][j - 1])
    
    def query(self, l, r):
        j = int(math.floor(math.log2(r - l + 1)))
        return min(self.st[l][j], self.st[r - (1 << j) + 1][j])

总的来说,平方根分解和稀疏表都是高效解决范围最小查询的方法。我们可以根据具体问题的复杂度和性能要求选择合适的算法。