heap
This commit is contained in:
parent
83061c4658
commit
00717dd038
4 changed files with 387 additions and 46 deletions
101
heap.c
101
heap.c
|
|
@ -1,45 +1,78 @@
|
||||||
#include "heap.h"
|
#include "heap.h"
|
||||||
#include "status_codes.h"
|
#include "status_codes.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
// #unteasted
|
heap* create_heap(int size, int (*comparison)(const void* a, const void* b))
|
||||||
int heap_push(heap* h, heap_record* in)
|
|
||||||
{
|
{
|
||||||
if (h->length >= h->capacity) {
|
heap* out = malloc(sizeof(heap));
|
||||||
return NO_SPACE;
|
out->data = malloc(sizeof(void*) * size);
|
||||||
}
|
out->length = 0;
|
||||||
|
out->capacity = size;
|
||||||
|
out->compare = comparison;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
int current_index = h->length;
|
void destroy_heap(heap* h)
|
||||||
|
{
|
||||||
|
free(h->data);
|
||||||
|
free(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(void** a, void** b)
|
||||||
|
{
|
||||||
|
void* tmp = *a;
|
||||||
|
*a = *b;
|
||||||
|
*b = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int heap_push(heap* h, void* in)
|
||||||
|
{
|
||||||
|
int current = h->length;
|
||||||
|
void** d = h->data;
|
||||||
h->length++;
|
h->length++;
|
||||||
heap_record* last = h->location;
|
d[current] = in;
|
||||||
last += current_index;
|
if (current == 0) {
|
||||||
*last = *in;
|
|
||||||
|
|
||||||
if (current_index == 0) {
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
int parent = (current - 1) / 2;
|
||||||
int parent_index = (current_index - 1) / 2;
|
while (h->compare(d[current], d[parent]) == -1) {
|
||||||
// check if violated heap property
|
swap(&d[current], &d[parent]);
|
||||||
heap_record* parent = h->location;
|
current = parent;
|
||||||
parent += parent_index;
|
if (current == 0) {
|
||||||
heap_record* current = h->location;
|
break;
|
||||||
current += current_index;
|
}
|
||||||
// swim up
|
parent = (current - 1) / 2;
|
||||||
while (current_index != 0 && h->compare(parent, current) == 1) {
|
}
|
||||||
// current, parent = parent, current
|
return SUCCESS;
|
||||||
heap_record tmp = *current;
|
}
|
||||||
*current = *parent;
|
|
||||||
*parent = tmp;
|
int heap_pop(heap* h, void** out)
|
||||||
// current_index = parent_index
|
{
|
||||||
current_index = parent_index;
|
void** d = h->data;
|
||||||
// parent_index = (current_index -1 )/2
|
*out = d[0];
|
||||||
parent_index = (current_index - 1) / 2;
|
h->length--;
|
||||||
// current = loc[current_index]
|
if (h->length == 0) {
|
||||||
current = h->location;
|
return SUCCESS;
|
||||||
current += current_index;
|
}
|
||||||
// parent = loc[parent_index]
|
swap(&d[0], &d[h->length]);
|
||||||
parent = h->location;
|
int current = 0;
|
||||||
parent_index += parent_index;
|
int left = current * 2 + 1;
|
||||||
|
int right = current * 2 + 2;
|
||||||
|
while (true) {
|
||||||
|
int smallest = current;
|
||||||
|
if (left < h->length && h->compare(d[smallest], d[left]) == 1) {
|
||||||
|
smallest = left;
|
||||||
|
}
|
||||||
|
if (right < h->length && h->compare(d[smallest], d[right]) == 1) {
|
||||||
|
smallest = right;
|
||||||
|
}
|
||||||
|
if (smallest == current) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
swap(&d[smallest], &d[current]);
|
||||||
|
current = smallest;
|
||||||
|
left = current * 2 + 1;
|
||||||
|
right = current * 2 + 2;
|
||||||
}
|
}
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
heap.h
19
heap.h
|
|
@ -1,22 +1,19 @@
|
||||||
|
// doesn't ceck for user erros, so no veryfying that the void* supplied are valid
|
||||||
|
// unsafe for general use, but just fnuctional enough for the purposes of this project
|
||||||
|
// does not maintain ownership of its elements, they are managed by the caller
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "stdbool.h"
|
#include "stdbool.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int index;
|
void** data;
|
||||||
int buffer_id;
|
|
||||||
} heap_record;
|
|
||||||
|
|
||||||
// almost the same as typedef buffer, but the usage is different, so I think it can be repeated
|
|
||||||
typedef struct {
|
|
||||||
heap_record* location;
|
|
||||||
int length;
|
int length;
|
||||||
int capacity;
|
int capacity;
|
||||||
int (*compare)(const void* a, const void* b);
|
int (*compare)(const void* a, const void* b);
|
||||||
} heap;
|
} heap;
|
||||||
|
|
||||||
heap new_heap(int size);
|
heap* create_heap(int size, int (*comparison)(const void* a, const void* b));
|
||||||
// free_heap is just free(h->location), so it's kinda unnecessary
|
void destroy_heap(heap* h);
|
||||||
|
|
||||||
int heap_pop(heap* h, heap_record* out);
|
int heap_pop(heap* h, void** out);
|
||||||
int heap_push(heap* h, heap_record* in);
|
int heap_push(heap* h, void* in);
|
||||||
|
|
|
||||||
302
heap_test.c
Normal file
302
heap_test.c
Normal file
|
|
@ -0,0 +1,302 @@
|
||||||
|
// the tests are ai generated
|
||||||
|
#include "heap.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// Comparator for integers
|
||||||
|
int compare_ints(const void* a, const void* b)
|
||||||
|
{
|
||||||
|
int ia = *(const int*)a;
|
||||||
|
int ib = *(const int*)b;
|
||||||
|
return (ia > ib) - (ia < ib);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparator for doubles
|
||||||
|
int compare_doubles(const void* a, const void* b)
|
||||||
|
{
|
||||||
|
double da = *(const double*)a;
|
||||||
|
double db = *(const double*)b;
|
||||||
|
if (da < db)
|
||||||
|
return -1;
|
||||||
|
if (da > db)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test structure
|
||||||
|
typedef struct {
|
||||||
|
int priority;
|
||||||
|
char name[32];
|
||||||
|
} Task;
|
||||||
|
|
||||||
|
// Comparator for tasks (by priority)
|
||||||
|
int compare_tasks(const void* a, const void* b)
|
||||||
|
{
|
||||||
|
const Task* ta = (const Task*)a;
|
||||||
|
const Task* tb = (const Task*)b;
|
||||||
|
return (ta->priority > tb->priority) - (ta->priority < tb->priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_basic_push_pop()
|
||||||
|
{
|
||||||
|
printf("Test: Basic push and pop... ");
|
||||||
|
|
||||||
|
heap* h = create_heap(5, compare_ints);
|
||||||
|
int values[] = { 5, 3, 7, 1, 9 };
|
||||||
|
|
||||||
|
// Push all values
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
heap_push(h, &values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop and verify they come out in sorted order
|
||||||
|
void* out;
|
||||||
|
int expected[] = { 1, 3, 5, 7, 9 };
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
heap_pop(h, &out);
|
||||||
|
assert(*(int*)out == expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_heap(h);
|
||||||
|
printf("PASSED\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_single_element()
|
||||||
|
{
|
||||||
|
printf("Test: Single element... ");
|
||||||
|
|
||||||
|
heap* h = create_heap(1, compare_ints);
|
||||||
|
int val = 42;
|
||||||
|
|
||||||
|
heap_push(h, &val);
|
||||||
|
|
||||||
|
void* out;
|
||||||
|
heap_pop(h, &out);
|
||||||
|
assert(*(int*)out == 42);
|
||||||
|
|
||||||
|
destroy_heap(h);
|
||||||
|
printf("PASSED\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_already_sorted()
|
||||||
|
{
|
||||||
|
printf("Test: Already sorted input... ");
|
||||||
|
|
||||||
|
heap* h = create_heap(5, compare_ints);
|
||||||
|
int values[] = { 1, 2, 3, 4, 5 };
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
heap_push(h, &values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* out;
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
heap_pop(h, &out);
|
||||||
|
assert(*(int*)out == i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_heap(h);
|
||||||
|
printf("PASSED\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_reverse_sorted()
|
||||||
|
{
|
||||||
|
printf("Test: Reverse sorted input... ");
|
||||||
|
|
||||||
|
heap* h = create_heap(5, compare_ints);
|
||||||
|
int values[] = { 5, 4, 3, 2, 1 };
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
heap_push(h, &values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* out;
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
heap_pop(h, &out);
|
||||||
|
assert(*(int*)out == i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_heap(h);
|
||||||
|
printf("PASSED\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_duplicates()
|
||||||
|
{
|
||||||
|
printf("Test: Duplicate values... ");
|
||||||
|
|
||||||
|
heap* h = create_heap(7, compare_ints);
|
||||||
|
int values[] = { 3, 1, 4, 1, 5, 9, 2 };
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
heap_push(h, &values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* out;
|
||||||
|
int expected[] = { 1, 1, 2, 3, 4, 5, 9 };
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
heap_pop(h, &out);
|
||||||
|
assert(*(int*)out == expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_heap(h);
|
||||||
|
printf("PASSED\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_with_doubles()
|
||||||
|
{
|
||||||
|
printf("Test: Double precision values... ");
|
||||||
|
|
||||||
|
heap* h = create_heap(5, compare_doubles);
|
||||||
|
double values[] = { 3.14, 1.41, 2.71, 0.57, 1.61 };
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
heap_push(h, &values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* out;
|
||||||
|
double expected[] = { 0.57, 1.41, 1.61, 2.71, 3.14 };
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
heap_pop(h, &out);
|
||||||
|
assert(*(double*)out == expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_heap(h);
|
||||||
|
printf("PASSED\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_with_structs()
|
||||||
|
{
|
||||||
|
printf("Test: Custom struct (tasks by priority)... ");
|
||||||
|
|
||||||
|
heap* h = create_heap(4, compare_tasks);
|
||||||
|
Task tasks[] = {
|
||||||
|
{ 3, "Low priority" },
|
||||||
|
{ 1, "High priority" },
|
||||||
|
{ 2, "Medium priority" },
|
||||||
|
{ 1, "Also high priority" }
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
heap_push(h, &tasks[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* out;
|
||||||
|
int expected_priorities[] = { 1, 1, 2, 3 };
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
heap_pop(h, &out);
|
||||||
|
Task* t = (Task*)out;
|
||||||
|
assert(t->priority == expected_priorities[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_heap(h);
|
||||||
|
printf("PASSED\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_interleaved_operations()
|
||||||
|
{
|
||||||
|
printf("Test: Interleaved push/pop... ");
|
||||||
|
|
||||||
|
heap* h = create_heap(10, compare_ints);
|
||||||
|
int values[] = { 5, 3, 7, 1, 9, 2, 8, 4, 6, 0 };
|
||||||
|
|
||||||
|
// Push 5, pop 1, push 3 more, pop 2, etc.
|
||||||
|
heap_push(h, &values[0]); // 5
|
||||||
|
heap_push(h, &values[1]); // 3
|
||||||
|
heap_push(h, &values[2]); // 7
|
||||||
|
|
||||||
|
void* out;
|
||||||
|
heap_pop(h, &out);
|
||||||
|
assert(*(int*)out == 3);
|
||||||
|
|
||||||
|
heap_push(h, &values[3]); // 1
|
||||||
|
heap_push(h, &values[4]); // 9
|
||||||
|
heap_push(h, &values[5]); // 2
|
||||||
|
|
||||||
|
heap_pop(h, &out);
|
||||||
|
assert(*(int*)out == 1);
|
||||||
|
heap_pop(h, &out);
|
||||||
|
assert(*(int*)out == 2);
|
||||||
|
|
||||||
|
heap_push(h, &values[6]); // 8
|
||||||
|
heap_push(h, &values[7]); // 4
|
||||||
|
|
||||||
|
heap_pop(h, &out);
|
||||||
|
assert(*(int*)out == 4);
|
||||||
|
|
||||||
|
destroy_heap(h);
|
||||||
|
printf("PASSED\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_large_heap()
|
||||||
|
{
|
||||||
|
printf("Test: Large heap (1000 elements)... ");
|
||||||
|
|
||||||
|
heap* h = create_heap(1000, compare_ints);
|
||||||
|
int* values = malloc(1000 * sizeof(int));
|
||||||
|
|
||||||
|
// Insert values in random-ish order
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
values[i] = (i * 7 + 13) % 1000;
|
||||||
|
heap_push(h, &values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop all and verify sorted
|
||||||
|
void* out;
|
||||||
|
int prev = -1;
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
heap_pop(h, &out);
|
||||||
|
int curr = *(int*)out;
|
||||||
|
assert(curr >= prev);
|
||||||
|
prev = curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(values);
|
||||||
|
destroy_heap(h);
|
||||||
|
printf("PASSED\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_all_same_values()
|
||||||
|
{
|
||||||
|
printf("Test: All same values... ");
|
||||||
|
|
||||||
|
heap* h = create_heap(5, compare_ints);
|
||||||
|
int val = 42;
|
||||||
|
int values[5];
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
values[i] = val;
|
||||||
|
heap_push(h, &values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* out;
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
heap_pop(h, &out);
|
||||||
|
assert(*(int*)out == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_heap(h);
|
||||||
|
printf("PASSED\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
printf("Running Min Heap Tests\n");
|
||||||
|
printf("======================\n\n");
|
||||||
|
|
||||||
|
test_basic_push_pop();
|
||||||
|
test_single_element();
|
||||||
|
test_already_sorted();
|
||||||
|
test_reverse_sorted();
|
||||||
|
test_duplicates();
|
||||||
|
test_with_doubles();
|
||||||
|
test_with_structs();
|
||||||
|
test_interleaved_operations();
|
||||||
|
test_large_heap();
|
||||||
|
test_all_same_values();
|
||||||
|
|
||||||
|
printf("\n======================\n");
|
||||||
|
printf("All tests PASSED! ✓\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
11
makefile
11
makefile
|
|
@ -33,7 +33,16 @@ build/run/1.in: build/run build/main
|
||||||
run: build/main build/run/1.in build/run/tmp
|
run: build/main build/run/1.in build/run/tmp
|
||||||
build/main -i build/run/1.in -d build/run/tmp | less
|
build/main -i build/run/1.in -d build/run/tmp | less
|
||||||
|
|
||||||
|
build/heap_test.o: heap_test.c build
|
||||||
|
clang $(CFLAGS) -c heap_test.c -o build/heap_test.o
|
||||||
|
|
||||||
|
build/test: build/heap_test.o build/heap.o
|
||||||
|
clang $(CFLAGS) build/heap_test.o build/heap.o -o build/test
|
||||||
|
|
||||||
|
test: build/test
|
||||||
|
build/test
|
||||||
|
|
||||||
debug: build/main tests/1.in
|
debug: build/main tests/1.in
|
||||||
lldb -- build/main
|
lldb -- build/main
|
||||||
|
|
||||||
.PHONY: clean run debug
|
.PHONY: clean run debug test
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue