erlang各种有用的函数包括一些有用nif封装,还有一些性能测试case。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

148 lines
2.7 KiB

#include "priority_queue.h"
#include "erl_nif.h"
#include <stdlib.h>
#include <string.h>
// Algorithm described at : http://robin-thomas.github.io/min-heap/
#define left(x) 2 * x + 1
#define right(x) 2 * x + 2
#define parent(x) (x - 1) / 2
#define less(a, b) less_(heap_[a], heap_[b])
PriorityQueue::PriorityQueue(LessFun ls, UpdatePositionFun upd, DestroyElementFun dtor) :
capacity_(0),
length_(0),
heap_(NULL),
less_(ls),
update_pos_fun_(upd),
item_dtor_(dtor) { }
PriorityQueue::~PriorityQueue()
{
if(heap_)
{
for(int i = 0; i < length_; i++)
item_dtor_(heap_[i]);
enif_free(heap_);
}
}
bool PriorityQueue::insert(void* item)
{
int pos;
if (length_ == capacity_)
{
int new_capacity = (length_+1) * 2;
void** new_heap = reinterpret_cast<void**>(enif_alloc(sizeof(void*) * new_capacity));
if (!new_heap)
return false;
memcpy(new_heap, heap_, sizeof(void*)*length_);
enif_free(heap_);
heap_ = new_heap;
capacity_ = new_capacity;
}
pos = (length_)++;
set(pos, item);
bubble_down(pos);
return true;
}
bool PriorityQueue::remove(void* item, int pos)
{
if (pos >= length_)
return false;
if(heap_[pos] != item)
return false;
return remove(pos) == item;
}
void* PriorityQueue::remove(int pos)
{
if (pos >= length_)
return NULL;
void* item = heap_[pos];
length_--;
int ls = less(pos, length_);
set(pos, heap_[length_]);
if(ls)
bubble_up(pos);
else
bubble_down(pos);
// todo: resize down the heap in case we have a lot of empty slots
update_pos_fun_(item, -1);
return item;
}
void* PriorityQueue::peek()
{
if(length_ == 0)
return NULL;
return heap_[0];
}
// private
void PriorityQueue::set(int pos, void* item)
{
heap_[pos] = item;
update_pos_fun_(item, pos);
}
void PriorityQueue::pos_swap(int pos1, int pos2)
{
void* tmp = heap_[pos1];
set(pos1, heap_[pos2]);
set(pos2, tmp);
}
void PriorityQueue::bubble_down(int pos)
{
while(true)
{
int parent = parent(pos);
if (pos == 0 || less(parent, pos))
return;
pos_swap(pos, parent);
pos = parent;
}
}
void PriorityQueue::bubble_up(int pos)
{
while (true)
{
int left = left(pos);
int right = right(pos);
int smallest = pos;
if (left < length_ && less(left, smallest))
smallest = left;
if (right < length_ && less(right, smallest))
smallest = right;
if (smallest == pos)
return;
pos_swap(pos, smallest);
pos = smallest;
}
}