Add any() and remove_any() to ph.
These functions select the easiest-to-remove element in the heap, which is either the most recently inserted aux list element or the root. If no calls are made to first() or remove_first(), the behavior (and time complexity) is the same as for a LIFO queue.
This commit is contained in:
parent
e201e24904
commit
cc75c35db5
@ -198,8 +198,10 @@ struct { \
|
|||||||
a_attr void a_prefix##new(a_ph_type *ph); \
|
a_attr void a_prefix##new(a_ph_type *ph); \
|
||||||
a_attr bool a_prefix##empty(a_ph_type *ph); \
|
a_attr bool a_prefix##empty(a_ph_type *ph); \
|
||||||
a_attr a_type *a_prefix##first(a_ph_type *ph); \
|
a_attr a_type *a_prefix##first(a_ph_type *ph); \
|
||||||
|
a_attr a_type *a_prefix##any(a_ph_type *ph); \
|
||||||
a_attr void a_prefix##insert(a_ph_type *ph, a_type *phn); \
|
a_attr void a_prefix##insert(a_ph_type *ph, a_type *phn); \
|
||||||
a_attr a_type *a_prefix##remove_first(a_ph_type *ph); \
|
a_attr a_type *a_prefix##remove_first(a_ph_type *ph); \
|
||||||
|
a_attr a_type *a_prefix##remove_any(a_ph_type *ph); \
|
||||||
a_attr void a_prefix##remove(a_ph_type *ph, a_type *phn);
|
a_attr void a_prefix##remove(a_ph_type *ph, a_type *phn);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -223,6 +225,17 @@ a_prefix##first(a_ph_type *ph) { \
|
|||||||
ph_merge_aux(a_type, a_field, ph, a_cmp); \
|
ph_merge_aux(a_type, a_field, ph, a_cmp); \
|
||||||
return ph->ph_root; \
|
return ph->ph_root; \
|
||||||
} \
|
} \
|
||||||
|
a_attr a_type * \
|
||||||
|
a_prefix##any(a_ph_type *ph) { \
|
||||||
|
if (ph->ph_root == NULL) { \
|
||||||
|
return NULL; \
|
||||||
|
} \
|
||||||
|
a_type *aux = phn_next_get(a_type, a_field, ph->ph_root); \
|
||||||
|
if (aux != NULL) { \
|
||||||
|
return aux; \
|
||||||
|
} \
|
||||||
|
return ph->ph_root; \
|
||||||
|
} \
|
||||||
a_attr void \
|
a_attr void \
|
||||||
a_prefix##insert(a_ph_type *ph, a_type *phn) { \
|
a_prefix##insert(a_ph_type *ph, a_type *phn) { \
|
||||||
memset(&phn->a_field, 0, sizeof(phn(a_type))); \
|
memset(&phn->a_field, 0, sizeof(phn(a_type))); \
|
||||||
@ -266,15 +279,52 @@ a_prefix##remove_first(a_ph_type *ph) { \
|
|||||||
\
|
\
|
||||||
return ret; \
|
return ret; \
|
||||||
} \
|
} \
|
||||||
|
a_attr a_type * \
|
||||||
|
a_prefix##remove_any(a_ph_type *ph) { \
|
||||||
|
/* \
|
||||||
|
* Remove the most recently inserted aux list element, or the \
|
||||||
|
* root if the aux list is empty. This has the effect of \
|
||||||
|
* behaving as a LIFO (and insertion/removal is therefore \
|
||||||
|
* constant-time) if a_prefix##[remove_]first() are never \
|
||||||
|
* called. \
|
||||||
|
*/ \
|
||||||
|
if (ph->ph_root == NULL) { \
|
||||||
|
return NULL; \
|
||||||
|
} \
|
||||||
|
a_type *ret = phn_next_get(a_type, a_field, ph->ph_root); \
|
||||||
|
if (ret != NULL) { \
|
||||||
|
a_type *aux = phn_next_get(a_type, a_field, ret); \
|
||||||
|
phn_next_set(a_type, a_field, ph->ph_root, aux); \
|
||||||
|
if (aux != NULL) { \
|
||||||
|
phn_prev_set(a_type, a_field, aux, \
|
||||||
|
ph->ph_root); \
|
||||||
|
} \
|
||||||
|
return ret; \
|
||||||
|
} \
|
||||||
|
ret = ph->ph_root; \
|
||||||
|
ph_merge_children(a_type, a_field, ph->ph_root, a_cmp, \
|
||||||
|
ph->ph_root); \
|
||||||
|
return ret; \
|
||||||
|
} \
|
||||||
a_attr void \
|
a_attr void \
|
||||||
a_prefix##remove(a_ph_type *ph, a_type *phn) { \
|
a_prefix##remove(a_ph_type *ph, a_type *phn) { \
|
||||||
a_type *replace, *parent; \
|
a_type *replace, *parent; \
|
||||||
\
|
\
|
||||||
/* \
|
|
||||||
* We can delete from aux list without merging it, but we need \
|
|
||||||
* to merge if we are dealing with the root node. \
|
|
||||||
*/ \
|
|
||||||
if (ph->ph_root == phn) { \
|
if (ph->ph_root == phn) { \
|
||||||
|
/* \
|
||||||
|
* We can delete from aux list without merging it, but \
|
||||||
|
* we need to merge if we are dealing with the root \
|
||||||
|
* node and it has children. \
|
||||||
|
*/ \
|
||||||
|
if (phn_lchild_get(a_type, a_field, phn) == NULL) { \
|
||||||
|
ph->ph_root = phn_next_get(a_type, a_field, \
|
||||||
|
phn); \
|
||||||
|
if (ph->ph_root != NULL) { \
|
||||||
|
phn_prev_set(a_type, a_field, \
|
||||||
|
ph->ph_root, NULL); \
|
||||||
|
} \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
ph_merge_aux(a_type, a_field, ph, a_cmp); \
|
ph_merge_aux(a_type, a_field, ph, a_cmp); \
|
||||||
if (ph->ph_root == phn) { \
|
if (ph->ph_root == phn) { \
|
||||||
ph_merge_children(a_type, a_field, ph->ph_root, \
|
ph_merge_children(a_type, a_field, ph->ph_root, \
|
||||||
|
@ -142,6 +142,7 @@ TEST_BEGIN(test_ph_empty) {
|
|||||||
heap_new(&heap);
|
heap_new(&heap);
|
||||||
assert_true(heap_empty(&heap), "Heap should be empty");
|
assert_true(heap_empty(&heap), "Heap should be empty");
|
||||||
assert_ptr_null(heap_first(&heap), "Unexpected node");
|
assert_ptr_null(heap_first(&heap), "Unexpected node");
|
||||||
|
assert_ptr_null(heap_any(&heap), "Unexpected node");
|
||||||
}
|
}
|
||||||
TEST_END
|
TEST_END
|
||||||
|
|
||||||
@ -159,6 +160,13 @@ node_remove_first(heap_t *heap) {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static node_t *
|
||||||
|
node_remove_any(heap_t *heap) {
|
||||||
|
node_t *node = heap_remove_any(heap);
|
||||||
|
node->magic = 0;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
TEST_BEGIN(test_ph_random) {
|
TEST_BEGIN(test_ph_random) {
|
||||||
#define NNODES 25
|
#define NNODES 25
|
||||||
#define NBAGS 250
|
#define NBAGS 250
|
||||||
@ -204,6 +212,8 @@ TEST_BEGIN(test_ph_random) {
|
|||||||
for (k = 0; k < j; k++) {
|
for (k = 0; k < j; k++) {
|
||||||
heap_insert(&heap, &nodes[k]);
|
heap_insert(&heap, &nodes[k]);
|
||||||
if (i % 13 == 12) {
|
if (i % 13 == 12) {
|
||||||
|
assert_ptr_not_null(heap_any(&heap),
|
||||||
|
"Heap should not be empty");
|
||||||
/* Trigger merging. */
|
/* Trigger merging. */
|
||||||
assert_ptr_not_null(heap_first(&heap),
|
assert_ptr_not_null(heap_first(&heap),
|
||||||
"Heap should not be empty");
|
"Heap should not be empty");
|
||||||
@ -216,7 +226,7 @@ TEST_BEGIN(test_ph_random) {
|
|||||||
"Heap should not be empty");
|
"Heap should not be empty");
|
||||||
|
|
||||||
/* Remove nodes. */
|
/* Remove nodes. */
|
||||||
switch (i % 4) {
|
switch (i % 6) {
|
||||||
case 0:
|
case 0:
|
||||||
for (k = 0; k < j; k++) {
|
for (k = 0; k < j; k++) {
|
||||||
assert_u_eq(heap_validate(&heap), j - k,
|
assert_u_eq(heap_validate(&heap), j - k,
|
||||||
@ -264,12 +274,31 @@ TEST_BEGIN(test_ph_random) {
|
|||||||
prev = node;
|
prev = node;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
} case 4: {
|
||||||
|
for (k = 0; k < j; k++) {
|
||||||
|
node_remove_any(&heap);
|
||||||
|
assert_u_eq(heap_validate(&heap), j - k
|
||||||
|
- 1, "Incorrect node count");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} case 5: {
|
||||||
|
for (k = 0; k < j; k++) {
|
||||||
|
node_t *node = heap_any(&heap);
|
||||||
|
assert_u_eq(heap_validate(&heap), j - k,
|
||||||
|
"Incorrect node count");
|
||||||
|
node_remove(&heap, node);
|
||||||
|
assert_u_eq(heap_validate(&heap), j - k
|
||||||
|
- 1, "Incorrect node count");
|
||||||
|
}
|
||||||
|
break;
|
||||||
} default:
|
} default:
|
||||||
not_reached();
|
not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_ptr_null(heap_first(&heap),
|
assert_ptr_null(heap_first(&heap),
|
||||||
"Heap should be empty");
|
"Heap should be empty");
|
||||||
|
assert_ptr_null(heap_any(&heap),
|
||||||
|
"Heap should be empty");
|
||||||
assert_true(heap_empty(&heap), "Heap should be empty");
|
assert_true(heap_empty(&heap), "Heap should be empty");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user