Skip to content

Commit

Permalink
Merge pull request #5 from IgooorGP/feature/add-sorting
Browse files Browse the repository at this point in the history
Feature/add sorting
  • Loading branch information
ipeternella authored Sep 7, 2021
2 parents 0d9bf8c + 30da6bd commit 7214bec
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 3 deletions.
1 change: 1 addition & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ DerivePointerAlignment: false # pointer aligment needs
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
BreakBeforeBraces: Linux
AllowShortFunctionsOnASingleLine: false
107 changes: 107 additions & 0 deletions include/strukts/strukts_heap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* @file strukts_heap.h
*
* @brief Module that contains heap implementations.
*
* Binary heaps are ARRAY objects that represent binary trees and, as such, can provide
* efficient operations. There are two types of binary heaps:
*
* - max-heap: the root contains the largest element: a[parent(i)] >= a[i]
* - min-heap: the root contains the smallest element: a[parent(i)] <= a[i]
*/

#ifndef STRUKTS_HEAP_H
#define STRUKTS_HEAP_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include <stdlib.h>

/********************** MACROS **********************/
#define SWAP(arr, i, j) \
int old = arr[i]; \
arr[i] = arr[j]; \
arr[j] = old

/**
* Represents a max-heap data structure in which an array can be viewed as a binary tree.
*/
typedef struct _StruktsMaxHeap StruktsMaxHeap;

struct _StruktsMaxHeap {
int* array; /* pointer to a heap array */
size_t size;
};

/**
* Represents child metadata of some parent node. If the result is within
* the heap array's bondaries, valid = true. Otherwise, valid = false.
*/
typedef struct _StruktsHeapChildResult StruktsHeapChildResult;

struct _StruktsHeapChildResult {
int value;
size_t position;
bool valid;
};

/**
* Builds a new binary heap (max-heap) from a given array in-place. This method mutates
* the array contents to organize it according to the rules of a max-heap.
*
* @param a is the array in which the heap will be built upon;
* @param len is the size of the array;
*
* @return a struct of a max-heap.
*/
StruktsMaxHeap strukts_heap_maxheap_new(int a[], size_t len);

/**
* Gets the left (or right) child of a given parent node which is in index i. If the given parent
* node would have children beyond the array's bondaries, the StruktsHeapChildResult will contain
* the property 'valid' set to false. Otherwise, 'valid' is set to true and the child is in the
* heap. Roughly works like a 'maybe' monad.
*
* @param heap is a max-heap struct.
* @param parent_i is the index of the parent node whose child will be searched for.
* @param left_child boolean which indicates to get the left child; if false, returns the right
* child.
*
* @return StruktsHeapChildResult struct which will contain metadata of the child such as its value,
* its position and whether this is a valid result or not (within the bondaries of the array of the
* heap). If valid is set to false, both the value and position should not be used.
*/
StruktsHeapChildResult strukts_heap_get_child(StruktsMaxHeap heap, size_t parent_i,
bool left_child);

/**
* Gets the parent's value of a given child node that can be found in index i.
*
* @param heap is a max-heap struct.
* @param child_i is the index of the child node whose parent will be searched for.
*
* @return the value of the parent node.
*/
int strukts_heap_get_parent(StruktsMaxHeap heap, size_t child_i);

/**
* Heapifies (mutates) the heap's array in order to guarantee the max-heap structure in which
* any parent is always bigger/equal than its children. Hence:
*
* - heap->array[parent(i)] >= heap->array[i]
*
* Heapifying can start from any index i. If i = 0 (root), then it heapifies the whole array
* (tree).
*
* @param heap is a max-heap struct.
* @param parent_i is the index of the desired 'parent node' to begin heapifying from.
*/
void strukts_heap_max_heapify(StruktsMaxHeap heap, size_t parent_i);

#ifdef __cplusplus
}
#endif
#endif /* STRUKTS_HEAP_H */
14 changes: 12 additions & 2 deletions include/strukts/strukts_sorting.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ extern "C" {
#include <stdlib.h>

/**
* Performs an in-place sorting of the int array a using insertion sort. As insertion
* Performs an in-place sorting of an int array a using insertion sort. As insertion
* sort runs in O(n^2), the len param is an int and not a size_t to avoid some complications
* due to size_t underflows.
*
Expand All @@ -27,7 +27,7 @@ extern "C" {
bool strukts_sorting_insertionsort(int a[], int len);

/**
* Performs an in-place sorting of the int array using mergesort (algorithm based on the divide and
* Performs an in-place sorting of an int array using mergesort (algorithm based on the divide and
* conquer strategy).
*
* @param a is an array of integers which will be sorted in place.
Expand All @@ -37,6 +37,16 @@ bool strukts_sorting_insertionsort(int a[], int len);
*/
bool strukts_sorting_mergesort(int a[], size_t len);

/**
* Performs an in-place sorting of an int array using heapsort.
*
* @param a is an array of integers which will be sorted in place.
* @param len is the length of array.
*
* @return true if the sorting was successful; false, otherwise.
*/
bool strukts_sorting_heapsort(int a[], size_t len);

#ifdef __cplusplus
}
#endif
Expand Down
84 changes: 84 additions & 0 deletions src/strukts_heap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include "strukts_heap.h"

#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>

/********************** PUBLIC FUNCTIONS **********************/
StruktsMaxHeap strukts_heap_maxheap_new(int a[], size_t len)
{
StruktsMaxHeap max_heap = {.array = a, .size = len};

/*
* Heapifies the whole array. Notice that there's no need to go beyond
* the middle of the array (parent_i = len / 2), as heapifying requires
* fetching children from each parent and as children stay, roughly,
* at the index (2 * parent_i), these children would be invalid as they
* would be outside the heap array size boundaries.
*
* The loop uses a trick to avoid underflow of a size_t which is always >= 0.
*/
for (size_t i = 0; i <= len / 2; i++) {
size_t parent_i = len / 2 - i; /* trick for reverse iterating with size_t */
strukts_heap_max_heapify(max_heap, parent_i);
}

return max_heap;
}

StruktsHeapChildResult strukts_heap_get_child(StruktsMaxHeap heap, size_t parent_i, bool left_child)
{
StruktsHeapChildResult result = {.valid = false, .value = 0, .position = 0};
size_t child_position;

if (left_child)
child_position = 2 * parent_i + 1;
else
child_position = 2 * parent_i + 2;

result.position = child_position;

/* index goes beyond the heap array's final index (size - 1): return invalid result */
if (child_position > heap.size - 1)
return result;

result.value = heap.array[child_position];
result.valid = true;

return result;
}

int strukts_heap_get_parent(StruktsMaxHeap heap, size_t child_i)
{
if (child_i % 2 == 0) /* even indexes */
return heap.array[child_i / 2 - 1];

return heap.array[child_i / 2];
}

void strukts_heap_max_heapify(StruktsMaxHeap heap, size_t parent_i)
{
StruktsHeapChildResult left_child = strukts_heap_get_child(heap, parent_i, true);
StruktsHeapChildResult right_child = strukts_heap_get_child(heap, parent_i, false);
size_t largest_i = 0;

/*
* Compares the parent, heap.array[parent_i], against its children nodes. Here the
* biggest wins (parent, left or right child). If any of the children is bigger than
* its parent, then the parent is swapped with its child and heapify is recursively
* called on the parent's new position (to continue to make sure the max-heap is respected).
*/
if (left_child.valid && left_child.value > heap.array[parent_i])
largest_i = left_child.position;
else
largest_i = parent_i; /* parent is bigger than left child */

if (right_child.valid && right_child.value > heap.array[largest_i])
largest_i = right_child.position; /* right child is the biggest one */

/* one of the children is bigger than the parent: swap and keep heapifying */
if (largest_i != parent_i) {
SWAP(heap.array, parent_i, largest_i);
strukts_heap_max_heapify(heap, largest_i);
}
}
21 changes: 20 additions & 1 deletion src/strukts_sorting.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <stdbool.h>
#include <stdlib.h>

#include "strukts_heap.h"

/********************** MACROS **********************/
#define INF INT_MAX

Expand Down Expand Up @@ -59,7 +61,7 @@ static bool strukts_mergesort_merge(int a[], size_t p, size_t q, size_t r)
return true;
}

static inline bool strukts_mergesort_helper(int a[], size_t p, size_t r)
static bool strukts_mergesort_helper(int a[], size_t p, size_t r)
{
/*
* Implementation of divide and conquer strategy with mergesort. One point to notice
Expand Down Expand Up @@ -98,6 +100,23 @@ bool strukts_sorting_mergesort(int a[], size_t len)
return strukts_mergesort_helper(a, 0, len - 1);
}

bool strukts_sorting_heapsort(int a[], size_t len)
{
/* builds a max-heap structure from the array to be sorted */
StruktsMaxHeap heap = strukts_heap_maxheap_new(a, len);

/* keep swapping the first node (biggest) with the last element and reheapifying the array */
for (size_t i = 0; i < len - 1; i++) {
SWAP(heap.array, 0, heap.size - 1); /* swap the first node with the last one */
heap.size--;

/* reorganize the heap after the swap: possible smaller value at the root */
strukts_heap_max_heapify(heap, 0);
}

return true;
}

bool strukts_sorting_insertionsort(int a[], int len)
{
/* trivial case */
Expand Down
20 changes: 20 additions & 0 deletions tests/test_strukts_heap.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "gtest/gtest.h"
#include "strukts_heap.h"

namespace
{
TEST(STRUKTS_HEAP_SUITE, SHOULD_BUILD_HEAP_FROM_ARRAY)
{
/* arrange */
int a[] = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7};
size_t len = 10;

/* act */
StruktsMaxHeap heap = strukts_heap_maxheap_new(a, len);

/* assert */
int expected[] = {16, 14, 10, 8, 7, 9, 3, 2, 4, 1};

EXPECT_TRUE(0 == memcmp(heap.array, expected, len * sizeof(int)));
}
} // namespace
64 changes: 64 additions & 0 deletions tests/test_strukts_sorting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,68 @@ namespace
EXPECT_TRUE(0 == memcmp(a, sorted_a, len * sizeof(int)));
EXPECT_TRUE(success);
}

TEST(STRUKTS_SORTING_SUITE, SHOULD_SORT_ARRAY_WITH_HEAPSORT)
{
/* arrange */
int a[] = {5, 2, 4, 6, 1, 3};
size_t len = 6;

/* act */
bool success = strukts_sorting_heapsort(a, len);

/* assert */
int sorted_a[] = {1, 2, 3, 4, 5, 6};

EXPECT_TRUE(0 == memcmp(a, sorted_a, len * sizeof(int)));
EXPECT_TRUE(success);
}

TEST(STRUKTS_SORTING_SUITE, SHOULD_SORT_ARRAY_FEWER_ELEMENTS_WITH_HEAPSORT)
{
/* arrange */
int a[] = {5, 2, 2, 6, 1};
size_t len = 5;

/* act */
bool success = strukts_sorting_heapsort(a, len);

/* assert */
int sorted_a[] = {1, 2, 2, 5, 6};

EXPECT_TRUE(0 == memcmp(a, sorted_a, len * sizeof(int)));
EXPECT_TRUE(success);
}

TEST(STRUKTS_SORTING_SUITE, SHOULD_SORT_ARRAY_TWO_ELEMENTS_WITH_HEAPSORT)
{
/* arrange */
int a[] = {5, 2};
size_t len = 2;

/* act */
bool success = strukts_sorting_heapsort(a, len);

/* assert */
int sorted_a[] = {2, 5};

EXPECT_TRUE(0 == memcmp(a, sorted_a, len * sizeof(int)));
EXPECT_TRUE(success);
}

TEST(STRUKTS_SORTING_SUITE, SHOULD_SORT_ARRAY_ONE_ELEMENT_WITH_HEAPSORT)
{
/* arrange */
int a[] = {7};
size_t len = 1;

/* act */
bool success = strukts_sorting_heapsort(a, len);

/* assert */
int sorted_a[] = {7};

EXPECT_TRUE(0 == memcmp(a, sorted_a, len * sizeof(int)));
EXPECT_TRUE(success);
}
} // namespace

0 comments on commit 7214bec

Please sign in to comment.