セグメント木

区間に対する処理をするときによく使われる。$a_1,a_2,\ldots,a_n$の配列に対し、区間$a_i$~$a_j$の合計値や最小値を求めたいとか。

これを「区間$a_1$~$a_i$の合計値」に限定したものが、Binary Indexed Treeで、柔軟性と引き替えにメモリや計算効率が向上している。

点に対する更新と、区間に対するクエリ

class SegTreeMin:
    """
    以下のクエリを処理する
    1.update:  i番目の値をxに更新する
    2.get_min: 区間[l, r)の最小値を得る
    """

    def __init__(self, n):
        """
        :param n: 要素数
        """
        self.n = n
        # nより大きい2の冪数
        n2 = 1
        while n2 < n:
            n2 <<= 1
        self.n2 = n2
        self.tree = [float('inf')] * (n2 << 1)

    def update(self, i, x):
        """
        i番目の値をxに更新
        :param i: index(0-indexed)
        :param x: update value
        """
        i += self.n2
        self.tree[i] = x
        while i > 1:
            self.tree[i >> 1] = x = min(x, self.tree[i ^ 1])
            i >>= 1

    def get_min(self, a, b):
        """
        [a, b)の最小値を得る
        :param a: index(0-indexed)
        :param b: index(0-indexed)
        """
        return self._get_min(a, b, 1, 0, self.n2)

    def _get_min(self, a, b, k, l, r):
        """
        [a, b)の最小値を得る内部関数
        :param k: 現在調べている区間のtree内index
        :param l, r: kが表す区間の左右端index [l, r)
        :return: kが表す区間と[a, b)の共通区間内での最小値。共通区間を持たない場合はINF
        """
        # 範囲外ならINF
        if r <= a or b <= l:
            return float('inf')
        # [a,b)が完全に[l,r)を包含するならtree[k]の値を採用
        if a <= l and r <= b:
            return self.tree[k]
        # 一部だけ範囲内なら2つに分けて再帰的に調査
        m = (l + r) // 2
        return min(
            self._get_min(a, b, k << 1, l, m),
            self._get_min(a, b, (k << 1) + 1, m, r)
        )

# --------------------------
st = SegTreeMin(30)
st.update(10, 5)
st.update(20, 4)
print(st.get_min(5, 15)) # => 5
print(st.get_min(5, 25)) # => 4
st.update(11, 3)
print(st.get_min(5, 25)) # => 3

区間に対する更新と、区間に対するクエリ

区間足し込みという手法が使える。

programming_algorithm/data_structure/segment_tree.txt · 最終更新: 2017/10/08 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0