C++ 使用C++;标准库

C++ 使用C++;标准库,c++,algorithm,data-structures,stl,heap,C++,Algorithm,Data Structures,Stl,Heap,我有一个使用std::make_heap的堆: std::vector<int> v{1,2,3,5,9,20,3}; std::make_heap(v.begin(), v.end()); std::vector<int> v{1,2,3,5,9,20,3}; // create a min heap std::make_heap(v.begin(), v.end(), std::greater<int>{}); // set 4th element t

我有一个使用
std::make_heap
的堆:

std::vector<int> v{1,2,3,5,9,20,3};
std::make_heap(v.begin(), v.end());
std::vector<int> v{1,2,3,5,9,20,3};
// create a min heap
std::make_heap(v.begin(), v.end(), std::greater<int>{});

// set 4th element to 35 in O(log n)
set_heap_element(v.begin(), v.end(), v.begin() + 3, 35, std::greater<int>{});

// is the min-heap property satisfied?
assert(std::is_heap(v.begin(), v.end(), std::greater<int>{}));
在中的标准库中是否有方法在
O(logn)
时间中再次调整堆,其中
n
是容器的大小。基本上我在寻找heapify函数。我知道什么元素被改变了

我知道
std::make_heap
O(n log n)
time。我也经历了重复的问题,但这是不同的意义上,它是改变最大元素。对于这个问题,已经给出了
O(logn)
复杂性的解决方案


我试图更改堆中的任何随机元素

我也一直面临着想要一个“可更新堆”的问题。然而,最后,我没有编写自定义的可更新堆或类似的代码,而是稍微改变了解决方法

为了在不需要显式遍历堆的情况下保持对最佳元素的访问,可以使用要排序的元素的版本包装器。每个唯一的true元素都有一个版本计数器,每当元素发生更改时,该计数器都会增加。然后,堆中的每个包装器都携带元素的一个版本,即创建包装器时的版本:

struct HeapElemWrapper
{
    HeapElem * e;

    size_t version;        
    double priority;

    HeapElemWrapper(HeapElem * elem)
     : e(elem), version(elem->currentVersion), priority(0.0)
    {}

    bool upToDate() const
    {
        return version == e->currentVersion;
    }

    // operator for ordering with heap / priority queue:
    // smaller error -> higher priority
    bool operator<(const HeapElemWrapper & other) const
    {
        return this->priority> other.priority;
    }
};
struct-heaplemWrapper
{
HeapElem*e;
尺寸(t)版本;;
双重优先;
heaplemWrapper(heaplem*elem)
:e(元素),版本(元素->当前版本),优先级(0.0)
{}
bool update()常量
{
返回版本==e->currentVersion;
}
//用于使用堆/优先级队列排序的运算符:
//错误越小->优先级越高
布尔运算符优先级>其他.优先级;
}
};

当从堆中弹出最上面的元素时,您可以简单地检查这个包装器元素,看看它是否与原始元素是最新的。如果没有,只需处理它,然后弹出下一个。这种方法非常有效,我在其他应用程序中也看到过。您需要注意的唯一一件事是,您不时地(比如,每1000次插入左右)对堆进行一次遍历,以清除其中过时的元素。

我也一直面临需要“可更新堆”的问题。然而,最后,我没有编写自定义的可更新堆或类似的代码,而是稍微改变了解决方法

为了在不需要显式遍历堆的情况下保持对最佳元素的访问,可以使用要排序的元素的版本包装器。每个唯一的true元素都有一个版本计数器,每当元素发生更改时,该计数器都会增加。然后,堆中的每个包装器都携带元素的一个版本,即创建包装器时的版本:

struct HeapElemWrapper
{
    HeapElem * e;

    size_t version;        
    double priority;

    HeapElemWrapper(HeapElem * elem)
     : e(elem), version(elem->currentVersion), priority(0.0)
    {}

    bool upToDate() const
    {
        return version == e->currentVersion;
    }

    // operator for ordering with heap / priority queue:
    // smaller error -> higher priority
    bool operator<(const HeapElemWrapper & other) const
    {
        return this->priority> other.priority;
    }
};
struct-heaplemWrapper
{
HeapElem*e;
尺寸(t)版本;;
双重优先;
heaplemWrapper(heaplem*elem)
:e(元素),版本(元素->当前版本),优先级(0.0)
{}
bool update()常量
{
返回版本==e->currentVersion;
}
//用于使用堆/优先级队列排序的运算符:
//错误越小->优先级越高
布尔运算符优先级>其他.优先级;
}
};

当从堆中弹出最上面的元素时,您可以简单地检查这个包装器元素,看看它是否与原始元素是最新的。如果没有,只需处理它,然后弹出下一个。这种方法非常有效,我在其他应用程序中也看到过。您需要注意的唯一一件事是,您不时地(比如,每1000次插入左右)对堆进行一次遍历,以清除其中过时的元素。

如果我们仔细看一下您的陈述:

现在,我通过更改堆的一个随机元素来扰乱堆

对于在
O(log n)
中进行重定位,您只能直接“干扰”向量的后面或前面(以某种方式对应于插入或删除元素)。在这些情况下,可以通过和算法实现(再)重映射,这需要对数运行时间

就是后面,

v.back() = 35;
std::push_heap(v.begin(), v.end()); // heapify in O(log n)
或正面:

v.front() = 35;

// places the front at the back
std::pop_heap(v.begin(), v.end()); // O(log n)
// v.back() is now 35, but it does not belong to the heap anymore

// make the back belong to the heap again
std::push_heap(v.begin(), v.end()); // O(log n)
否则,需要使用重新调整整个向量,这需要线性运行时间


总结
无法使用标准库(即函数模板
std::push_heap
std::pop_heap
)修改堆的任意元素并在对数运行时间内实现堆化。但是,您始终可以自己实现堆的swim和sink操作,以便在对数运行时间内堆化。

如果我们仔细看一下您的语句:

现在,我通过更改堆的一个随机元素来扰乱堆

对于在
O(log n)
中进行重定位,您只能直接“干扰”向量的后面或前面(以某种方式对应于插入或删除元素)。在这些情况下,可以通过和算法实现(再)重映射,这需要对数运行时间

就是后面,

v.back() = 35;
std::push_heap(v.begin(), v.end()); // heapify in O(log n)
或正面:

v.front() = 35;

// places the front at the back
std::pop_heap(v.begin(), v.end()); // O(log n)
// v.back() is now 35, but it does not belong to the heap anymore

// make the back belong to the heap again
std::push_heap(v.begin(), v.end()); // O(log n)
否则,需要使用重新调整整个向量,这需要线性运行时间


总结
无法使用标准库(即函数模板
std::push_heap
std::pop_heap
)修改堆的任意元素并在对数运行时间内实现堆化。但是,您始终可以自己实现堆的swim和sink操作,以便在对数运行时间内进行堆处理。

您可以自己完成:

void modify_heap_element(std::vector<int> &heap, size_t index, int value)
{
    //while value is too large for its position, bubble up
    while(index > 0 && heap[(index-1)>>1] < value)
    {
        size_t parent = (index-1)>>1;
        heap[index]=heap[parent];
        index = parent;
    }
    //while value is too large for its position sift down
    for (;;)
    {
        size_t left=index*2+1;
        size_t right=left+1;
        if (left >= heap.size())
            break;
        size_t bigchild = (right >= heap.size() || heap[right] < heap[left] ?
                           left : right );
        if (!(value < heap[bigchild]))
           break;
        heap[index]=heap[bigchild];
        index = bigchild;
    }
    heap[index] = value;
}
void modify_heap_元素(std::vector&heap,size\t index,int值)
{
//当价值对其位置而言太大时,泡沫就会出现
而(索引>0&&heap[(索引-1)>>1]>1;
堆[索引]=堆[父];
指数=父母;
}
//当值对于其位置太大时,请向下筛选
对于(;;)
{
assert(std::is_heap(v.begin(), v.end())); 
std::vector<int> v{1,2,3,5,9,20,3};
// create a min heap
std::make_heap(v.begin(), v.end(), std::greater<int>{});

// set 4th element to 35 in O(log n)
set_heap_element(v.begin(), v.end(), v.begin() + 3, 35, std::greater<int>{});

// is the min-heap property satisfied?
assert(std::is_heap(v.begin(), v.end(), std::greater<int>{}));