Implement a sparse array / set.
This commit is contained in:
parent
4003fa1b78
commit
3aedc61da4
|
@ -4,7 +4,7 @@ project(libds)
|
|||
set_property(GLOBAL PROPERTY C_STANDARD 90)
|
||||
ADD_COMPILE_OPTIONS(-pedantic -Wall)
|
||||
|
||||
add_library(ds STATIC src/libds.c src/vec.c src/ht.c src/ll.c)
|
||||
add_library(ds STATIC src/libds.c src/vec.c src/ht.c src/ll.c src/sprs.c)
|
||||
add_executable(libds src/main.c)
|
||||
|
||||
target_include_directories(ds PUBLIC include)
|
||||
|
|
142
include/sprs.h
Normal file
142
include/sprs.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
#ifndef LIBDS_SPRS_HEADER
|
||||
#define LIBDS_SPRS_HEADER
|
||||
|
||||
#include "libds.h"
|
||||
|
||||
/**
|
||||
* Sparse set implementation with the capacity to store data.
|
||||
* Sparse sets are initially defined as having two arrays of integers,
|
||||
* one "dense" array, into which new elements are added sequentially, and another
|
||||
* "sparse" array into which the elements' indexes in the dense array are put at
|
||||
* their index.
|
||||
*
|
||||
* For instance, after adding the elements 1, 0, and 3 to a sparse array,
|
||||
* it may look like:
|
||||
* (Size) (Capacity)
|
||||
* | |
|
||||
* [1 ][0 ][3 ][..][..][..] (Dense)
|
||||
* [1 ][0 ][..][2 ][..][..] (Sparse)
|
||||
* As seen in the above representation, the element in the dense array at index 0 is 1,
|
||||
* and the element at index 1 in the sparse array points to its location in the dense array.
|
||||
* It's very quick to check whether an element is part of this sparse set:
|
||||
* if sparse[element] < size && dense[sparse[element]] == element.
|
||||
* Iteration is also O(n), thanks to the dense array, and no initial memory clearing is required.
|
||||
*
|
||||
* The below implementation adds an additional piece of data to the sparse array, a void* so that an item
|
||||
* can be associated with an integer index and stored / retrieved / ismember'ed together with it.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A single element in a sparse array.
|
||||
*/
|
||||
struct sprs_element_s {
|
||||
/**
|
||||
* The index of this element in the dense array.
|
||||
*/
|
||||
int index;
|
||||
/**
|
||||
* A piece of data (optional) associated with this index.
|
||||
*/
|
||||
void* data;
|
||||
};
|
||||
|
||||
/**
|
||||
* A sparse array / set.
|
||||
*/
|
||||
struct sprs_s {
|
||||
/**
|
||||
* The maximum size of the set. This is the limit for integer indices.
|
||||
*/
|
||||
int capacity;
|
||||
/**
|
||||
* The current size of the sparse set, and the next available index in the dense array.
|
||||
*/
|
||||
int size;
|
||||
|
||||
/**
|
||||
* The dense array.
|
||||
* This is a pointer so that an array of a certain size can be allocated as needed.
|
||||
*/
|
||||
int* dense;
|
||||
/**
|
||||
* The sparse array.
|
||||
* This is a pointer so that an array of a certain size can be allocated as needed.
|
||||
*/
|
||||
struct sprs_element_s* sparse;
|
||||
};
|
||||
|
||||
typedef struct sprs_s sprs;
|
||||
|
||||
/**
|
||||
* Initializes a sparse set with default values.
|
||||
* This does not allocate any memory for the arrays.
|
||||
* @param sprs the sparse set / array to initialize.
|
||||
*/
|
||||
void sprs_init(sprs* sprs);
|
||||
/**
|
||||
* Allocates the necessary memory for a sparse set's
|
||||
* dense and sparse arrays.
|
||||
* @param sprs the sparse set for which memory should be allocated.
|
||||
* @param size the maximum capacity of the sparse set to use.
|
||||
* @return LIBDS_SUCCESS if all goes well, or LIBDS_MALLOC if an allocation failed.
|
||||
*/
|
||||
libds_result sprs_setup(sprs* sprs, int size);
|
||||
/**
|
||||
* Frees memory allocated by the sparse set, and resets its capacity and size.
|
||||
* @param sprs the sparse set to free.
|
||||
*/
|
||||
void sprs_free(sprs* sprs);
|
||||
|
||||
/**
|
||||
* Stores a value in the sparse set under a given index.
|
||||
* @param sprs the sparse set into which to store the value.
|
||||
* @param index the index at which to store the value
|
||||
* @param data the value to store.
|
||||
*/
|
||||
void sprs_put(sprs* sprs, int index, void* data);
|
||||
/**
|
||||
* Checks if the sparse set contains data under a given index.
|
||||
* @param sprs the sparse set to check.
|
||||
* @param index the index for which to check.
|
||||
* @return 1 if the index exists, 0 if not.
|
||||
*/
|
||||
int sprs_contains(sprs* sprs, int index);
|
||||
/**
|
||||
* Gets the value stored under the given sparse set index.
|
||||
* This will check for whether the index is in the sparse set first,
|
||||
* and return NULL if it's not.
|
||||
* Please not that this exhibit the !exact same! behavior if a NULL was stored
|
||||
* under the index or if the index was never populated. use sprs_contains to
|
||||
* check whether the index is populated if this is important.
|
||||
* @param sprs the sparse set to check.
|
||||
* @param index the index from under which to retrieve the value.
|
||||
* @return the value stored under the index, or NULL if there is nothing there.
|
||||
*/
|
||||
void* sprs_get(sprs* sprs, int index);
|
||||
|
||||
/**
|
||||
* Runs through every element in the sparse set, and compares it against the
|
||||
* given data using the given comparison function. If the comparison function returns
|
||||
* true, returns the element that was passed to it. If the comparison function returns
|
||||
* true for no element, returns NULL.
|
||||
* @param sprs the sparse set to iterate through.
|
||||
* @param data the data to compare elements against
|
||||
* @param compare the comparison function
|
||||
* @return the first element that is matched by the comparison function, or NULL if none are matched.
|
||||
*/
|
||||
void* sprs_find(sprs* sprs, void* data, compare_func compare);
|
||||
/**
|
||||
* Runs through every element in the sparse set, and compares it against the
|
||||
* given data using the given comparison function. If the comparison function returns
|
||||
* true, calls the foreach function, passing it the element and the variable argument list.
|
||||
* Stops if any foreach function returns a nonzero code.
|
||||
* @param sprs the sparse set to perform the operation on
|
||||
* @param data the data to pass the comparison function
|
||||
* @param compare the comparison function operating on the data and each element in the list.
|
||||
* @param foreach the function to be called on every element recognized by the comparison function
|
||||
* @param ... variable arguments to be passed on to the foreach function
|
||||
* @return 0 if all goes well, or the first nonzero code returned by foreach.
|
||||
*/
|
||||
int sprs_foreach(sprs* sprs, void* data, compare_func compare, foreach_func foreach, ...);
|
||||
|
||||
#endif
|
70
src/sprs.c
Normal file
70
src/sprs.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include "sprs.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
void sprs_init(sprs* sprs){
|
||||
sprs->capacity = 0;
|
||||
sprs->size = 0;
|
||||
sprs->dense = NULL;
|
||||
sprs->sparse = NULL;
|
||||
}
|
||||
libds_result sprs_setup(sprs* sprs, int size){
|
||||
libds_result result = LIBDS_SUCCESS;
|
||||
sprs->sparse = malloc(sizeof(*(sprs->sparse)) * size);
|
||||
sprs->dense = malloc(sizeof(*(sprs->dense)) * size);
|
||||
if(sprs->sparse == NULL || sprs->dense == NULL){
|
||||
free(sprs->sparse);
|
||||
free(sprs->dense);
|
||||
result = LIBDS_MALLOC;
|
||||
} else {
|
||||
sprs->capacity = size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void sprs_free(sprs* sprs){
|
||||
free(sprs->sparse);
|
||||
free(sprs->dense);
|
||||
sprs->capacity = 0;
|
||||
sprs->size = 0;
|
||||
sprs->dense = NULL;
|
||||
sprs->sparse = NULL;
|
||||
}
|
||||
void sprs_put(sprs* sprs, int index, void* data){
|
||||
if(index < sprs->capacity && sprs->size < sprs->capacity){
|
||||
sprs->dense[sprs->size++] = index;
|
||||
sprs->sparse[index].index = sprs->size - 1;
|
||||
sprs->sparse[index].data = data;
|
||||
}
|
||||
}
|
||||
int sprs_contains(sprs* sprs, int index){
|
||||
return sprs->sparse[index].index < sprs->size && sprs->dense[sprs->sparse[index].index] == index;
|
||||
}
|
||||
void* sprs_get(sprs* sprs, int index){
|
||||
void* data = NULL;
|
||||
if(sprs_contains(sprs, index)){
|
||||
data = sprs->sparse[index].data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
void* sprs_find(sprs* sprs, void* data, compare_func compare){
|
||||
int index = 0;
|
||||
void* to_return = NULL;
|
||||
for(; index < sprs->size && to_return == NULL; index++){
|
||||
if(compare(data, sprs->sparse[sprs->dense[index]].data)) {
|
||||
to_return = sprs->sparse[sprs->dense[index]].data;
|
||||
}
|
||||
}
|
||||
return to_return;
|
||||
}
|
||||
int sprs_foreach(sprs* sprs, void* data, compare_func compare, foreach_func foreach, ...){
|
||||
int index = 0;
|
||||
int return_code = 0;
|
||||
va_list args;
|
||||
for(; index < sprs->size && return_code == 0; index++){
|
||||
if(compare(data, sprs->sparse[sprs->dense[index]].data)) {
|
||||
va_start(args, foreach);
|
||||
return_code = foreach(sprs->sparse[sprs->dense[index]].data, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
return return_code;
|
||||
}
|
Loading…
Reference in New Issue
Block a user