这不是作业。
我正在使用一个小的“优先级队列”(目前作为数组实现)来存储具有最小值的最后N个项目。这有点慢 - O(N)项目插入时间。当前实现跟踪数组中的最大项,并丢弃任何不适合数组的项,但我仍希望进一步减少操作数。
寻找符合以下要求的优先级队列算法:
我最初考虑使用二进制堆(它们可以通过数组轻松实现),但显然当数组不能再增长时它们表现不佳。链接列表和数组将需要额外的时间来移动东西。 stl优先级队列增长并使用动态分配(但可能错误)。
那么,还有其他想法吗?
- EDIT--
我对STL实现不感兴趣。由于大量的函数调用,STL实现(由少数人建议)比当前使用的线性数组慢一点。
我对优先级队列算法感兴趣,而不是实现。
答案 0 :(得分:14)
基于数组的堆似乎非常适合您的目的。我不确定你为什么拒绝他们。
您使用最大堆。
假设您有一个N元素堆(实现为数组),其中包含到目前为止看到的N个最小元素。
当一个元素进入时,你检查最大值(O(1)时间),如果它更大则拒绝。
如果输入的值较低,则将root修改为新值并对此更改值进行筛选 - 最差情况为O(log N)时间。
筛选过程很简单:从root开始,在每个步骤中将此值与其较大的子项交换,直到max-heap属性恢复为止。
因此,如果您使用std :: priority_queue,则不必执行任何您可能需要的删除。根据std :: priority_queue的实现,这可能导致内存分配/释放。
所以你可以得到如下代码:
平均而言,您可能不必一直向下筛选新值,并且可能比O(logn)平均插入时间更好(尽管我还没有尝试过证明它)。
您只需分配大小为N的数组一次,并且通过交换数组的元素完成任何插入,因此之后没有动态内存分配。
查看包含heapify和sift-down伪代码的wiki页面:http://en.wikipedia.org/wiki/Heapsort
答案 1 :(得分:8)
将std::priority_queue
与头部的最大项目一起使用。对于每个新项目,如果它是头部项目>=
,则丢弃它,否则弹出头部项目并插入新项目。
旁注:标准容器只有在你成长的情况下才会增长。只要在插入新项目之前删除一个项目(当然,在它达到最大大小之后),就不会发生这种情况。
答案 2 :(得分:1)
我工作的大多数优先级队列都基于链表。如果您具有预定数量的优先级,则可以通过具有链接列表数组(每个优先级一个链接列表)轻松创建具有O(1)插入的优先级队列。具有相同优先级的项目当然会退化为FIFO,但这可以被认为是可接受的。
添加和删除会变得类似(您的API可能会有所不同)......
listItemAdd (&list[priLevel], &item); /* Add to tail */
pItem = listItemRemove (&list[priLevel]); /* Remove from head */
获取队列中的第一项然后成为查找具有最高优先级的非空链接列表的问题。这可能是O(N),但你可以使用一些技巧加快速度。
希望这有帮助。
答案 3 :(得分:0)
如果优先级较小且固定,则可以为每个优先级使用ring-buffer。如果对象很大,那将导致浪费空间,但是如果它们的大小与指针/索引相当,那么在对象中存储附加指针的变体可能会以相同的方式增加数组的大小。
或者你可以在数组中使用简单的单链表并存储2 * M + 1个指针/索引,一个指向第一个空闲节点,其他对指向每个优先级的头尾。在那种情况下,你将不得不平均比较。 O(M)在用O(1)取出下一个节点之前。插入将采用O(1)。
答案 4 :(得分:0)
如果以最大大小构造STL优先级队列(可能来自使用占位符初始化的向量),然后在插入之前检查大小(如果有必要,则在事先删除项目),在插入操作期间永远不会进行动态分配。 STL实现非常有效。
答案 5 :(得分:0)
Matters Computational请参阅第158页。实现本身非常好,您甚至可以稍微调整它,而不会降低其可读性。例如,当你计算左边的孩子时:
int left = i / 2;
你可以像这样计算右边的孩子:
int right = left + 1;
答案 6 :(得分:0)
找到解决方案(“差异”在代码中表示“优先级”,maxRememberedResults为255(可以是任意(2 ^ n - 1)):
template <typename T> inline void swap(T& a, T& b){
T c = a;
a = b;
b = c;
}
struct MinDifferenceArray{
enum{maxSize = maxRememberedResults};
int size;
DifferenceData data[maxSize];
void add(const DifferenceData& val){
if (size >= maxSize){
if(data[0].difference <= val.difference)
return;
data[0] = val;
for (int i = 0; (2*i+1) < maxSize; ){
int next = 2*i + 1;
if (data[next].difference < data[next+1].difference)
next++;
if (data[i].difference < data[next].difference)
swap(data[i], data[next]);
else
break;
i = next;
}
}
else{
data[size++] = val;
for (int i = size - 1; i > 0;){
int parent = (i-1)/2;
if (data[parent].difference < data[i].difference){
swap(data[parent], data[i]);
i = parent;
}
else
break;
}
}
}
void clear(){
size = 0;
}
MinDifferenceArray()
:size(0){
}
};
我们得到O(log(N))插入作为最坏的情况。
与昵称为“Moron”的用户提供的解决方案相同。 感谢大家的回复。
P.S。显然没有足够睡眠的编程并不是一个好主意。
答案 7 :(得分:0)
最好使用std :: array和堆算法来实现自己的类。
`template<class T, int fixed_size = 5>
class fixed_size_arr_pqueue_v2
{
std::array<T, fixed_size> _data;
int _size = 0;
int parent(int i)
{
return (i - 1)/2;
}
void heapify(int i, bool downward = false)
{
int l = 2*i + 1;
int r = 2*i + 2;
int largest = 0;
if (l < size() && _data[l] > _data[i])
largest = l;
else
largest = i;
if (r < size() && _data[r] > _data[largest])
largest = r;
if (largest != i)
{
std::swap(_data[largest], _data[i]);
if (!downward)
heapify(parent(i));
else
heapify(largest, true);
}
}
public:
void push(T &d)
{
if (_size == fixed_size)
{
//min elements in a max heap lies at leaves only.
auto minItr = std::min_element(begin(_data) + _size/2, end(_data));
auto minPos {minItr - _data.begin()};
auto min { *minItr};
if (d > min)
{
_data.at(minPos) = d;
if (_data[parent(minPos)] > d)
{
//this is unlikely to happen in our case? as this position is a leaf?
heapify(minPos, true);
}
else
heapify(parent(minPos));
}
return ;
}
_data.at(_size++) = d;
std::push_heap(_data.begin(), _data.begin() + _size);
}
T pop()
{
T d = _data.front();
std::pop_heap(_data.begin(), _data.begin() + _size);
_size--;
return d;
}
T top()
{
return _data.front();
}
int size() const
{
return _size;
}
};`