c++segment-tree

C++ - How to return index of minimum element in range query?


I am trying to implement range queries on a segment tree (a binary tree). I am trying to modify the query function so that it can return the index of the minimum value over a range instead of the actual minimum value (which it is currently returning).

Tree, update and query functions:

int length; // This is the length of the array I am querying
int tree[1000000]; // This is a binary tree

// You call update at the start with the index you want to change and the value you want to change it to
void update(int index, int value, int position = 1, int currentL = 0, int currentR = length-1) {
    if (currentL == currentR) {
        tree[position] = value;
        return;
    }
    int mid = (currentL+currentR)/2;
    if (index <= mid) {
        update(index, value, position*2, currentL, mid);
    } else {
        update(index, value, position*2+1, mid+1, currentR);
    }
    tree[c] = min(tree[position*2], tree[position*2+1]);
}

// You call query with the range you want to query over
int query(int qL, int qR, int c = 1, int cL = 0, int cR = length-1) {
    if (qL <= cL && cR <= qR) {
        return tree[c];
    }
    int mid = (cL+cR)/2;
    int ans = 10005;
    if (qL <= mid) {
        ans = min(ans, query(qL, qR, 2*c, cL, mid));
    }
    if (qR > mid) {
        ans = min(ans, query(qL, qR, 2*c+1, mid+1, cR));
    }
    return ans;
}

Is there a way to change the query function so that it returns not just the minimum value of a range in the array, but also the index of that value?


Solution

  • You can save index together with the value in segment tree by using, e.g., std::pair<int, int> as tree node. If at the leafs we define the pair to be { value, index } for corresponding array element, other nodes should contain pairs { minimal value, its leftmost index } for corresponding range. Since comparison operators for std::pair are defined as lexicographical over constituents, using std::min (which is based on operator< if comparator is not explicitly provided) the same way as in your code will lead to choosing, for each node/range, minimal of child-nodes/subrange minimums together with its index (smaller one will be chosen if minimums are equal, because second pair element is used for comparison then), so it will produce desired accumulation of ranges. Code:

    std::pair<int, int> tree[1000000];
    
    void update(int index, int value, int position = 1, int currentL = 0, int currentR = length-1) {
        if (currentL == currentR) {
            tree[position] = { value, index };
            return;
        }
        int mid = (currentL+currentR)/2;
        if (index <= mid) {
            update(index, value, position*2, currentL, mid);
        } else {
            update(index, value, position*2+1, mid+1, currentR);
        }
        tree[c] = min(tree[position*2], tree[position*2+1]);
    }
    
    std::pair<int, int> query(int qL, int qR, int c = 1, int cL = 0, int cR = length-1) {
        if (qL <= cL && cR <= qR) {
            return tree[c];
        }
        int mid = (cL+cR)/2;
        std::pair<int, int> ans = { std::numeric_limits<int>::max(), std::numeric_limits<int>::max() };
        if (qL <= mid) {
            ans = min(ans, query(qL, qR, 2*c, cL, mid));
        }
        if (qR > mid) {
            ans = min(ans, query(qL, qR, 2*c+1, mid+1, cR));
        }
        return ans;
    }
    

    Of course, to make code more readable custom struct with proper data member names may be used, but then you have to define comparison operator(s) yourself (or pass comparators to std::min). Also note that that I used std::numerical_limits (doc) for initial ans value since it's more generic and idiomatic.