From 6b9fd818f8d409c0f38c4c45b9ffd24191c1ce1a Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Sat, 24 Sep 2016 02:49:24 +0000 Subject: [PATCH] added qse_arr_updateheap(), qse_arr_deleteheap() --- qse/include/qse/cmn/arr.h | 22 +++- qse/lib/cmn/arr.c | 228 +++++++++++++++++++++++++------------- qse/lib/cmn/tmr.c | 4 +- qse/samples/cmn/arr01.c | 51 +++++++-- qse/samples/cmn/bh02.cpp | 33 +++++- 5 files changed, 238 insertions(+), 100 deletions(-) diff --git a/qse/include/qse/cmn/arr.h b/qse/include/qse/cmn/arr.h index e62aa2c7..68e404d1 100644 --- a/qse/include/qse/cmn/arr.h +++ b/qse/include/qse/cmn/arr.h @@ -90,8 +90,13 @@ typedef void (*qse_arr_freeer_t) ( /** * The qse_arr_comper_t type defines a key comparator that is called when * the arry needs to compare data. A linear dynamic array is created with a - * default comparator that performs bitwise comparison. + * default comparator that performs bitwise comparison. * + * The default comparator compares data in a memcmp-like fashion. + * It is not suitable when you want to implement a heap of numbers + * greater than a byte size. You must implement a comparator that + * takes the whole element and performs comparison in such a case. + * * The comparator should return 0 if the data are the same, a negative * integer if the first data is less than the second data, a positive * integer otherwise. @@ -259,7 +264,8 @@ QSE_EXPORT qse_arr_comper_t qse_arr_getcomper ( /** * The qse_arr_setcomper() function specifies how to compare two elements * for equality test. The comparator @a comper must return 0 if two elements - * compared are equal, or a non-zero number otherwise. + * compared are equal, 1 if the first element is greater than the + * second, -1 if the second element is greater than the first. */ QSE_EXPORT void qse_arr_setcomper ( qse_arr_t* arr /**< arr */, @@ -428,6 +434,18 @@ QSE_EXPORT void qse_arr_popheap ( qse_arr_t* arr ); +QSE_EXPORT void qse_arr_deleteheap ( + qse_arr_t* arr, + qse_size_t index +); + +QSE_EXPORT qse_size_t qse_arr_updateheap ( + qse_arr_t* arr, + qse_size_t index, + void* dptr, + qse_size_t dlen +); + #if defined(__cplusplus) } #endif diff --git a/qse/lib/cmn/arr.c b/qse/lib/cmn/arr.c index f4b0a324..edf1e710 100644 --- a/qse/lib/cmn/arr.c +++ b/qse/lib/cmn/arr.c @@ -43,6 +43,8 @@ #define DPTR(slot) ((slot)->val.ptr) #define DLEN(slot) ((slot)->val.len) +/* the default comparator is not proper for number comparision. + * the first different byte decides whice side is greater */ static int default_comparator (arr_t* arr, const void* dptr1, size_t dlen1, const void* dptr2, size_t dlen2) @@ -52,8 +54,11 @@ static int default_comparator (arr_t* arr, return 1; */ - size_t min = (dlen1 < dlen2)? dlen1: dlen2; - int n = QSE_MEMCMP (dptr1, dptr2, TOB(arr,min)); + int n; + size_t min; + + min = (dlen1 < dlen2)? dlen1: dlen2; + n = QSE_MEMCMP (dptr1, dptr2, TOB(arr,min)); if (n == 0 && dlen1 != dlen2) { n = (dlen1 > dlen2)? 1: -1; @@ -378,8 +383,7 @@ size_t qse_arr_insert (arr_t* arr, size_t pos, void* dptr, size_t dlen) if (capa <= mincapa) { - if (arr->freeer) - arr->freeer (arr, DPTR(slot), DLEN(slot)); + if (arr->freeer) arr->freeer (arr, DPTR(slot), DLEN(slot)); QSE_MMGR_FREE (arr->mmgr, slot); return QSE_ARR_NIL; } @@ -392,8 +396,7 @@ size_t qse_arr_insert (arr_t* arr, size_t pos, void* dptr, size_t dlen) if (pos >= arr->capa || arr->size >= arr->capa) { /* the buffer is not still enough after resizing */ - if (arr->freeer) - arr->freeer (arr, DPTR(slot), DLEN(slot)); + if (arr->freeer) arr->freeer (arr, DPTR(slot), DLEN(slot)); QSE_MMGR_FREE (arr->mmgr, slot); return QSE_ARR_NIL; } @@ -431,7 +434,7 @@ size_t qse_arr_update (arr_t* arr, size_t pos, void* dptr, size_t dlen) if (dptr == DPTR(c) && dlen == DLEN(c)) { /* updated to the same data */ - if (arr->keeper) arr->keeper (arr, dptr, dlen); + if (arr->keeper) arr->keeper (arr, dptr, dlen); } else { @@ -439,8 +442,7 @@ size_t qse_arr_update (arr_t* arr, size_t pos, void* dptr, size_t dlen) slot_t* slot = alloc_slot (arr, dptr, dlen); if (slot == QSE_NULL) return QSE_ARR_NIL; - if (arr->freeer != QSE_NULL) - arr->freeer (arr, DPTR(c), DLEN(c)); + if (arr->freeer) arr->freeer (arr, DPTR(c), DLEN(c)); QSE_MMGR_FREE (arr->mmgr, c); arr->slot[pos] = slot; @@ -465,8 +467,7 @@ size_t qse_arr_delete (arr_t* arr, size_t index, size_t count) if (c != QSE_NULL) { - if (arr->freeer != QSE_NULL) - arr->freeer (arr, DPTR(c), DLEN(c)); + if (arr->freeer) arr->freeer (arr, DPTR(c), DLEN(c)); QSE_MMGR_FREE (arr->mmgr, c); arr->slot[i] = QSE_NULL; @@ -498,8 +499,7 @@ size_t qse_arr_uplete (arr_t* arr, size_t index, size_t count) if (c != QSE_NULL) { - if (arr->freeer != QSE_NULL) - arr->freeer (arr, DPTR(c), DLEN(c)); + if (arr->freeer) arr->freeer (arr, DPTR(c), DLEN(c)); QSE_MMGR_FREE (arr->mmgr, c); arr->slot[i] = QSE_NULL; @@ -608,93 +608,163 @@ void qse_arr_popstack (arr_t* arr) #define HEAP_LEFT(x) ((x)*2 + 1) #define HEAP_RIGHT(x) ((x)*2 + 2) -size_t qse_arr_pushheap (arr_t* arr, void* dptr, size_t dlen) +size_t sift_up (arr_t* arr, size_t index) { - size_t cur, par; + size_t parent; int n; - /* add a value to the bottom */ - cur = arr->size; - if (qse_arr_insert (arr, cur, dptr, dlen) == QSE_ARR_NIL) - return QSE_ARR_NIL; + if (index > 0) + { + parent = HEAP_PARENT(index); + n = arr->comper (arr, + DPTR(arr->slot[index]), DLEN(arr->slot[index]), + DPTR(arr->slot[parent]), DLEN(arr->slot[parent])); + if (n > 0) + { + slot_t* tmp; - while (cur != 0) + tmp = arr->slot[index]; + + while (1) + { + arr->slot[index] = arr->slot[parent]; + + index = parent; + parent = HEAP_PARENT(parent); + + if (index <= 0) break; + + n = arr->comper (arr, + DPTR(tmp), DLEN(tmp), + DPTR(arr->slot[parent]), DLEN(arr->slot[parent])); + if (n <= 0) break; + } + + arr->slot[index] = tmp; + } + } + return index; +} + +size_t sift_down (arr_t* arr, size_t index) +{ + size_t base; + + base = arr->size / 2; + + if (index < base) /* at least 1 child is under the 'index' position */ { slot_t* tmp; - /* compare with the parent */ - par = HEAP_PARENT(cur); - n = arr->comper (arr, - DPTR(arr->slot[cur]), DLEN(arr->slot[cur]), - DPTR(arr->slot[par]), DLEN(arr->slot[par])); - if (n <= 0) break; /* ok */ + tmp = arr->slot[index]; - /* swap the current with the parent */ - tmp = arr->slot[cur]; - arr->slot[cur] = arr->slot[par]; - arr->slot[par] = tmp; + do + { + qse_size_t left, right, child; + int n; - cur = par; + left= HEAP_LEFT(index); + right = HEAP_RIGHT(index); + + if (right < arr->size) + { + n = arr->comper (arr, + DPTR(arr->slot[right]), DLEN(arr->slot[right]), + DPTR(arr->slot[left]), DLEN(arr->slot[left])); + child = (n > 0)? right: left; + } + else + { + child = left; + } + + n = arr->comper (arr, + DPTR(tmp), DLEN(tmp), + DPTR(arr->slot[child]), DLEN(arr->slot[child])); + if (n > 0) break; + + arr->slot[index] = arr->slot[child]; + index = child; + } + while (index < base); + + arr->slot[index] = tmp; } + return index; +} + +size_t qse_arr_pushheap (arr_t* arr, void* dptr, size_t dlen) +{ + size_t index; + + /* add a value at the back of the array */ + index = arr->size; + if (qse_arr_insert (arr, index, dptr, dlen) == QSE_ARR_NIL) return QSE_ARR_NIL; + + QSE_ASSERT (arr->size == index + 1); + + /* move the item upto the top if it's greater than the parent items */ + sift_up (arr, index); return arr->size; } void qse_arr_popheap (arr_t* arr) { - size_t cur, child; + QSE_ASSERT (arr->size > 0); + qse_arr_deleteheap (arr, 0); +} + +void qse_arr_deleteheap (arr_t* arr, size_t index) +{ slot_t* tmp; QSE_ASSERT (arr->size > 0); + QSE_ASSERT (index < arr->size); - /* destroy the top */ - tmp = arr->slot[0]; + /* remember the item to destroy */ + tmp = arr->slot[index]; + + arr->size = arr->size - 1; + if (arr->size > 0 && index != arr->size) + { + int n; + + /* move the last item to the deleting position */ + arr->slot[index] = arr->slot[arr->size]; + + /* move it up if the last item is greater than the item to be deleted, + * move it down otherwise. */ + n = arr->comper (arr, + DPTR(arr->slot[index]), DLEN(arr->slot[index]), + DPTR(tmp), DLEN(tmp)); + if (n > 0) sift_up (arr, index); + else if (n < 0) sift_down (arr, index); + } + + /* destroy the actual item */ if (arr->freeer) arr->freeer (arr, DPTR(tmp), DLEN(tmp)); QSE_MMGR_FREE (arr->mmgr, tmp); - /* move the last item to the top position also shrink the size */ - arr->slot[0] = arr->slot[--arr->size]; - - if (arr->size <= 1) return; /* only 1 element. nothing further to do */ - - for (cur = 0; cur < arr->size; cur = child) - { - size_t left, right; - int n; - - left = HEAP_LEFT(cur); - right = HEAP_RIGHT(cur); - - if (left >= arr->size) - { - /* the left child does not exist. - * reached the bottom. abort exchange */ - break; - } - - if (right >= arr->size) - { - /* the right child does not exist. only the left */ - child = left; - } - else - { - /* get the larger child of the two */ - n = arr->comper (arr, - DPTR(arr->slot[left]), DLEN(arr->slot[left]), - DPTR(arr->slot[right]), DLEN(arr->slot[right])); - child = (n > 0)? left: right; - } - - /* compare the current one with the child */ - n = arr->comper (arr, - DPTR(arr->slot[cur]), DLEN(arr->slot[cur]), - DPTR(arr->slot[child]), DLEN(arr->slot[child])); - if (n > 0) break; /* current one is larger. stop exchange */ - - /* swap the current with the child */ - tmp = arr->slot[cur]; - arr->slot[cur] = arr->slot[child]; - arr->slot[child] = tmp; - } + /* empty the last slot */ + arr->slot[arr->size] = QSE_NULL; +} + +size_t qse_arr_updateheap (qse_arr_t* arr, qse_size_t index, void* dptr, qse_size_t dlen) +{ + slot_t* tmp; + int n; + + tmp = arr->slot[index]; + QSE_ASSERT (tmp != QSE_NULL); + + n = arr->comper (arr, dptr, dlen, DPTR(tmp), DLEN(tmp)); + if (n) + { + if (qse_arr_update (arr, index, dptr, dlen) == QSE_ARR_NIL) return QSE_ARR_NIL; + if (n > 0) sift_up (arr, index); + else sift_down (arr, index); + } + + return index; } diff --git a/qse/lib/cmn/tmr.c b/qse/lib/cmn/tmr.c index c0cf681c..1dc67f35 100644 --- a/qse/lib/cmn/tmr.c +++ b/qse/lib/cmn/tmr.c @@ -137,7 +137,7 @@ static qse_tmr_index_t sift_down (qse_tmr_t* tmr, qse_tmr_index_t index, int not { qse_size_t base = tmr->size / 2; - if (index < base) /* at least 1 child is under the 'index' positmrn */ + if (index < base) /* at least 1 child is under the 'index' position */ { qse_tmr_event_t item; qse_size_t old_index; @@ -149,7 +149,7 @@ static qse_tmr_index_t sift_down (qse_tmr_t* tmr, qse_tmr_index_t index, int not { qse_size_t left, right, younger; - left= HEAP_LEFT(index); + left = HEAP_LEFT(index); right = HEAP_RIGHT(index); if (right < tmr->size && YOUNGER_THAN(&tmr->event[right], &tmr->event[left])) diff --git a/qse/samples/cmn/arr01.c b/qse/samples/cmn/arr01.c index b6611d13..5d98d923 100644 --- a/qse/samples/cmn/arr01.c +++ b/qse/samples/cmn/arr01.c @@ -372,6 +372,14 @@ static int test4 () qse_arr_comper_t default_comparator; +static int integer_comparator (qse_arr_t* arr, + const void* dptr1, size_t dlen1, + const void* dptr2, size_t dlen2) +{ + return (*(int*)dptr1 > *(int*)dptr2)? 1: + (*(int*)dptr1 < *(int*)dptr2)? -1: 0; +} + static int inverse_comparator (qse_arr_t* arr, const void* dptr1, size_t dlen1, const void* dptr2, size_t dlen2) @@ -382,7 +390,7 @@ static int inverse_comparator (qse_arr_t* arr, static int test5 () { qse_arr_t* s1; - int i, j; + int i, j, oldv, newv; s1 = qse_arr_open (QSE_MMGR_GETDFL(), 0, 3); if (s1 == QSE_NULL) @@ -393,55 +401,74 @@ static int test5 () qse_arr_setcopier (s1, QSE_ARR_COPIER_INLINE); qse_arr_setscale (s1, QSE_SIZEOF(i)); + qse_arr_setcomper (s1, integer_comparator); /* inverse the comparator to implement min-heap */ default_comparator = qse_arr_getcomper (s1); qse_arr_setcomper (s1, inverse_comparator); - for (i = 0; i < 25; i++) + for (i = 0; i < 2500; i++) { - j = rand () % 100; + j = rand () % 1000; qse_arr_pushheap (s1, &j, 1); } qse_printf (QSE_T("arr size => %lu\n"), QSE_ARR_SIZE(s1)); qse_arr_walk (s1, walker3, QSE_NULL); + oldv = 0; while (QSE_ARR_SIZE(s1) > 10 ) { - qse_printf (QSE_T("top => %d\n"), *(int*)QSE_ARR_DPTR(s1,0)); + newv = *(int*)QSE_ARR_DPTR(s1,0); + qse_printf (QSE_T("top => %d prevtop => %d\n"), newv, oldv); + QSE_ASSERT (newv >= oldv); qse_arr_popheap (s1); + oldv = newv; } - for (i = 0; i < 25; i++) + for (i = 0; i < 2500; i++) { - j = rand () % 100; + j = rand () % 1000; qse_arr_pushheap (s1, &j, 1); } qse_printf (QSE_T("arr size => %lu\n"), QSE_ARR_SIZE(s1)); qse_arr_walk (s1, walker3, QSE_NULL); - while (QSE_ARR_SIZE(s1)) + oldv = 0; + while (QSE_ARR_SIZE(s1) > 0) { - qse_printf (QSE_T("top => %d\n"), *(int*)QSE_ARR_DPTR(s1,0)); + newv = *(int*)QSE_ARR_DPTR(s1,0); + qse_printf (QSE_T("top => %d prevtop => %d\n"), newv, oldv); + QSE_ASSERT (newv >= oldv); qse_arr_popheap (s1); + oldv = newv; } + /* back to max-heap */ qse_arr_setcomper (s1, default_comparator); - for (i = 0; i < 25; i++) + for (i = 0; i < 2500; i++) { - j = rand () % 100; + j = rand () % 1000; qse_arr_pushheap (s1, &j, 1); } + j = 88888888; + qse_arr_updateheap (s1, QSE_ARR_SIZE(s1) / 2, &j, 1); + j = -123; + qse_arr_updateheap (s1, QSE_ARR_SIZE(s1) / 2, &j, 1); qse_printf (QSE_T("arr size => %lu\n"), QSE_ARR_SIZE(s1)); qse_arr_walk (s1, walker3, QSE_NULL); - while (QSE_ARR_SIZE(s1)) + + oldv = 99999999; + while (QSE_ARR_SIZE(s1) > 0) { - qse_printf (QSE_T("top => %d\n"), *(int*)QSE_ARR_DPTR(s1,0)); + newv = *(int*)QSE_ARR_DPTR(s1,0); + qse_printf (QSE_T("top => %d prevtop => %d\n"), newv, oldv); + QSE_ASSERT (newv <= oldv); qse_arr_popheap (s1); + oldv = newv; } diff --git a/qse/samples/cmn/bh02.cpp b/qse/samples/cmn/bh02.cpp index cb2e0820..64a52ede 100644 --- a/qse/samples/cmn/bh02.cpp +++ b/qse/samples/cmn/bh02.cpp @@ -1,11 +1,14 @@ #include - #include +#include #include #include #include #include + +//#define MAX_HEAP + class Julia { public: @@ -60,7 +63,11 @@ public: } #endif +#if defined(MAX_HEAP) bool operator> (const Julia& q) const { return *this->x > *q.x; } +#else + bool operator> (const Julia& q) const { return *this->x < *q.x; } +#endif int* x; }; @@ -71,20 +78,36 @@ int main () JuliaHeap jh; qse_uint32_t x; qse_ntime_t nt; + int oldval, newval; qse_gettime (&nt); x = nt.sec + nt.nsec; - for (int i = 0; i < 100; i++) + for (int i = 0; i < 2500; i++) { - x = qse_rand31(x); - jh.insert (Julia((int)x % 100)); + //x = qse_rand31(x); + x = rand(); + jh.insert (Julia((int)x % 1000)); } +#if defined(MAX_HEAP) + oldval = 9999999; +#else + oldval = 0; +#endif while (!jh.isEmpty()) { - printf ("%d\n", *jh.getValueAt(0).x); + newval = *jh.getValueAt(0).x; + printf ("%d oldval => %d\n", newval, oldval); +#if defined(MAX_HEAP) + QSE_ASSERT (newval <= oldval); +#else + QSE_ASSERT (newval >= oldval); +#endif jh.remove (0); + oldval = newval; } + + return 0; }