Heapify in logarithmic time using the C++ standard library

前端 未结 4 416
灰色年华
灰色年华 2021-01-06 00:35

I have a heap using std::make_heap:

std::vector v{1,2,3,5,9,20,3};
std::make_heap(v.begin(), v.end());

now I update

4条回答
  •  情歌与酒
    2021-01-06 01:05

    It's not possible to modify an arbitrary element of the heap in logarithmic running time without violating the heap property by just using the function templates std::pop_heap() and std::push_heap() that the standard library provides.

    However, you can define your own STL-like function template, set_heap_element(), for that purpose:

    template
    void set_heap_element(RandomIt first, RandomIt last, RandomIt pos, T value, Cmp cmp)
    {
        const auto n = last - first;
        *pos = std::move(value); // replace previous value
    
        auto i = pos - first;
        using std::swap;
    
        // percolate up
        while (i > 0) { // non-root node
            auto parent_it = first + (i-1)/2;
    
            if (cmp(*pos, *parent_it))
                break; // parent node satisfies the heap-property 
    
            swap(*pos, *parent_it); // swap with parent
            pos = parent_it;
            i = pos - first;
        }
    
        // percolate down
        while (2*i + 1 < n) { // non-leaf node, since it has a left child
            const auto lidx = 2*i + 1, ridx = 2*i + 2;
    
            auto lchild_it = first + lidx; 
            auto rchild_it = ridx < n? first + ridx: last;
    
            auto it = pos;
            if (cmp(*it, *lchild_it))
                it = lchild_it;
            if (rchild_it != last && cmp(*it, *rchild_it))
                it = rchild_it;
    
            if (pos == it)
                break; // node satisfies the heap-property
    
            swap(*pos, *it); // swap with child
            pos = it;
            i = pos - first;
        }
    }
    

    Then, you can provide the following simplified overload of set_heap_element() for a max heap:

    #include  // std::less
    
    template
    void set_heap_element(RandomIt first, RandomIt last, RandomIt pos, T value) {
        return set_heap_element(first, last, pos, value, std::less{});
    }
    

    This overload uses a std::less object as the comparison function object for the original function template.


    Example

    In your max-heap example, set_heap_element() could be used as follows:

    std::vector v{1,2,3,5,9,20,3};
    std::make_heap(v.begin(), v.end());
    
    // set 4th element to 35 in O(log n)
    set_heap_element(v.begin(), v.end(), v.begin() + 3, 35);
    

    You could use std::is_heap(), which takes linear time, whenever you want to check whether the max-heap property is still satisfied by v after setting an element with the set_heap_element() function template above:

    assert(std::is_heap(v.begin(), v.end())); 
    

    What about min heaps?

    You can achieve the same for a min heap by passing a std::greater object as the last argument of the function calls to std::make_heap(), set_heap_element() and std::is_heap():

    std::vector v{1,2,3,5,9,20,3};
    // create a min heap
    std::make_heap(v.begin(), v.end(), std::greater{});
    
    // set 4th element to 35 in O(log n)
    set_heap_element(v.begin(), v.end(), v.begin() + 3, 35, std::greater{});
    
    // is the min-heap property satisfied?
    assert(std::is_heap(v.begin(), v.end(), std::greater{}));
    

提交回复
热议问题