util: tree-based associative container
This commit is contained in:
parent
6832a76265
commit
e5ba03599b
80
util/test_tree.c
Normal file
80
util/test_tree.c
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
//$CC -g test_tree.c tree.c ../driver/random/random.c
|
||||||
|
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "stdio.h"
|
||||||
|
|
||||||
|
#include "tree.h"
|
||||||
|
|
||||||
|
|
||||||
|
tree_Tree *tree;
|
||||||
|
|
||||||
|
void insert(int a) {
|
||||||
|
bool new;
|
||||||
|
int *d = tree_Insert(tree, a, &new);
|
||||||
|
if (new)
|
||||||
|
*d = 1;
|
||||||
|
else
|
||||||
|
(*d)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count(int a) {
|
||||||
|
int *d = tree_Find(tree, a);
|
||||||
|
if (!d)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return *d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete (int a) {
|
||||||
|
tree_Node *node = tree_FindNode(tree, a);
|
||||||
|
if (!node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
(*(int *)node->data)--;
|
||||||
|
if ((*(int *)node->data) == 0)
|
||||||
|
tree_Delete(tree, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
tree = tree_Create(sizeof(int));
|
||||||
|
|
||||||
|
insert(1);
|
||||||
|
insert(2);
|
||||||
|
insert(3);
|
||||||
|
insert(6);
|
||||||
|
insert(1);
|
||||||
|
|
||||||
|
printf("%d\n", count(2));
|
||||||
|
printf("%d\n", count(4));
|
||||||
|
printf("%d\n", count(1));
|
||||||
|
delete (1);
|
||||||
|
printf("%d\n", count(1));
|
||||||
|
delete (1);
|
||||||
|
printf("%d\n", count(1));
|
||||||
|
delete (1);
|
||||||
|
printf("%d\n", count(1));
|
||||||
|
delete (2);
|
||||||
|
printf("%d\n", count(1));
|
||||||
|
|
||||||
|
insert(7);
|
||||||
|
insert(10);
|
||||||
|
insert(54);
|
||||||
|
insert(18);
|
||||||
|
insert(63);
|
||||||
|
insert(39);
|
||||||
|
|
||||||
|
tree_Node *i = tree_FirstNode(tree);
|
||||||
|
while (i) {
|
||||||
|
printf(" %d", (int)i->key);
|
||||||
|
i = tree_Node_Next(i);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
i = tree_LastNode(tree);
|
||||||
|
while (i) {
|
||||||
|
printf(" %d", (int)i->key);
|
||||||
|
i = tree_Node_Previous(i);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
164
util/tree.c
Normal file
164
util/tree.c
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
|
||||||
|
#include "tree.h"
|
||||||
|
#include "tree_internal.h"
|
||||||
|
|
||||||
|
#include "../main.h"
|
||||||
|
#include "../memory/memory.h"
|
||||||
|
#include "../driver/random/random.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Create allocates and creates a new default Tree object.
|
||||||
|
tree_Tree *tree_Create(uintptr_t objectSize) {
|
||||||
|
tree_Tree *t = kMalloc(sizeof(tree_Tree));
|
||||||
|
t->objectSize = objectSize;
|
||||||
|
t->size = 0;
|
||||||
|
t->root = 0;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline tree_Node *__tree_NewNode(tree_Tree *t, uintptr_t key, tree_Node *father, uintptr_t internal) {
|
||||||
|
t->size++;
|
||||||
|
|
||||||
|
tree_Node *node = kMalloc(sizeof(tree_Node) - 1 + t->objectSize);
|
||||||
|
node->left = node->right = 0;
|
||||||
|
node->father = father;
|
||||||
|
node->key = key;
|
||||||
|
node->internal = internal;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __tree_DestroyNodes(tree_Node *node) {
|
||||||
|
if (node->left)
|
||||||
|
__tree_DestroyNodes(node->left);
|
||||||
|
if (node->right)
|
||||||
|
__tree_DestroyNodes(node->right);
|
||||||
|
kFree(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_Destroy(tree_Tree *tree) {
|
||||||
|
if (tree->root)
|
||||||
|
__tree_DestroyNodes(tree->root);
|
||||||
|
kFree(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Will not return NULL
|
||||||
|
// SysV ABI because of the 6 arguments
|
||||||
|
SYSV_ABI static tree_Node *__tree_InsertNodes(tree_Tree *t, tree_Node *node, tree_Node *father, uintptr_t key, tree_Node **result, bool *added) {
|
||||||
|
if (!node) {
|
||||||
|
if (added)
|
||||||
|
*added = true;
|
||||||
|
return *result = __tree_NewNode(t, key, father, random_Rand() ^ key);
|
||||||
|
} else if (key < node->key) {
|
||||||
|
node->left = __tree_InsertNodes(t, node->left, node, key, result, added);
|
||||||
|
return node;
|
||||||
|
} else if (key > node->key) {
|
||||||
|
node->right = __tree_InsertNodes(t, node->right, node, key, result, added);
|
||||||
|
return node;
|
||||||
|
} else {
|
||||||
|
if (added)
|
||||||
|
*added = false;
|
||||||
|
*result = node;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_Node *tree_InsertNode(tree_Tree *t, uintptr_t key, bool *added) {
|
||||||
|
tree_Node *result;
|
||||||
|
t->root = __tree_InsertNodes(t, t->root, 0, key, &result, added);
|
||||||
|
|
||||||
|
if (*added)
|
||||||
|
__tree_treap_Adjust(result, &t->root);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *tree_Insert(tree_Tree *t, uintptr_t key, bool *added) {
|
||||||
|
return tree_InsertNode(t, key, added)->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static tree_Node *__tree_FindNode(tree_Node *node, uintptr_t key) {
|
||||||
|
if (!node)
|
||||||
|
return NULL;
|
||||||
|
else if (key < node->key)
|
||||||
|
return __tree_FindNode(node->left, key);
|
||||||
|
else if (key > node->key)
|
||||||
|
return __tree_FindNode(node->right, key);
|
||||||
|
else
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_Node *tree_FindNode(tree_Tree *t, uintptr_t key) {
|
||||||
|
return __tree_FindNode(t->root, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *tree_Find(tree_Tree *t, uintptr_t key) {
|
||||||
|
tree_Node *node = tree_FindNode(t, key);
|
||||||
|
if (!node)
|
||||||
|
return NULL;
|
||||||
|
else
|
||||||
|
return node->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void tree_Delete(tree_Tree *t, tree_Node *node) {
|
||||||
|
while (node->left && node->right)
|
||||||
|
if (node->left->internal < node->right->internal)
|
||||||
|
__tree_Rotate(node->left, &t->root);
|
||||||
|
else
|
||||||
|
__tree_Rotate(node->right, &t->root);
|
||||||
|
|
||||||
|
if (node == t->root)
|
||||||
|
t->root = (node->left ? node->left : node->right);
|
||||||
|
__tree_Connect(node->father, (node->left ? node->left : node->right), __tree_Tell(node));
|
||||||
|
|
||||||
|
kFree(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tree_Node *tree_FirstNode(tree_Tree *tree) {
|
||||||
|
tree_Node *result = tree->root;
|
||||||
|
while (result->left)
|
||||||
|
result = result->left;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_Node *tree_LastNode(tree_Tree *tree) {
|
||||||
|
tree_Node *result = tree->root;
|
||||||
|
while (result->right)
|
||||||
|
result = result->right;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tree_Node *tree_Node_Next(tree_Node *node) {
|
||||||
|
if (node->right) {
|
||||||
|
tree_Node *result = node->right;
|
||||||
|
while (result->left)
|
||||||
|
result = result->left;
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
tree_Node *result = node;
|
||||||
|
while (result->father && __tree_Tell(result) == __tree_Right)
|
||||||
|
result = result->father;
|
||||||
|
return result->father;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node_Previous returns the previous node. Returns NULL if first.
|
||||||
|
tree_Node *tree_Node_Previous(tree_Node *node) {
|
||||||
|
if (node->left) {
|
||||||
|
tree_Node *result = node->left;
|
||||||
|
while (result->right)
|
||||||
|
result = result->right;
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
tree_Node *result = node;
|
||||||
|
while (result->father && __tree_Tell(result) == __tree_Left)
|
||||||
|
result = result->father;
|
||||||
|
return result->father;
|
||||||
|
}
|
||||||
|
}
|
75
util/tree.h
Normal file
75
util/tree.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct __tree_Node {
|
||||||
|
uintptr_t key; // node key
|
||||||
|
struct __tree_Node *left, *right; // left and right sons
|
||||||
|
struct __tree_Node *father; // father node
|
||||||
|
uintptr_t internal; // internal data for balanced trees
|
||||||
|
char data[1]; // placeholder for object data
|
||||||
|
} tree_Node;
|
||||||
|
|
||||||
|
// Tree is a basic tree-based associative container.
|
||||||
|
//
|
||||||
|
// Right now it's a Treap.
|
||||||
|
typedef struct {
|
||||||
|
uintptr_t objectSize; // size in bytes of the object
|
||||||
|
tree_Node *root; // root of the tree, NULL if empty
|
||||||
|
uintptr_t size; // number of objects in the tree
|
||||||
|
} tree_Tree;
|
||||||
|
|
||||||
|
// Create allocates and creates a new default Tree object.
|
||||||
|
tree_Tree *tree_Create(uintptr_t objectSize);
|
||||||
|
|
||||||
|
// Destroy properly frees all data related to the structure, and itself.
|
||||||
|
void tree_Destroy(tree_Tree *tree);
|
||||||
|
|
||||||
|
// Insert inserts a new object (or locates an existing one).
|
||||||
|
//
|
||||||
|
// If *added is not NULL, it is set to true if the key does
|
||||||
|
// not exist and is actually added.
|
||||||
|
//
|
||||||
|
// Newly allocated data is not zeroed, nor initialized in any way.
|
||||||
|
//
|
||||||
|
// Returns the pointer to the newly allocated (or existing) data.
|
||||||
|
void *tree_Insert(tree_Tree *tree, uintptr_t key, bool *added);
|
||||||
|
|
||||||
|
// InsertNode does the same as Insert, but returns Node* instead of data.
|
||||||
|
tree_Node *tree_InsertNode(tree_Tree *tree, uintptr_t key, bool *added);
|
||||||
|
|
||||||
|
// Find locates an existing object by its key.
|
||||||
|
//
|
||||||
|
// Returns NULL if the object does not exist.
|
||||||
|
void *tree_Find(tree_Tree *tree, uintptr_t key);
|
||||||
|
|
||||||
|
// FindNode returns an existing tree node by its key.
|
||||||
|
//
|
||||||
|
// Used for iterating the tree objects.
|
||||||
|
tree_Node *tree_FindNode(tree_Tree *tree, uintptr_t key);
|
||||||
|
|
||||||
|
// FirstNode returns the first node in increasing order.
|
||||||
|
tree_Node *tree_FirstNode(tree_Tree *tree);
|
||||||
|
|
||||||
|
// LastNode returns the last node in increasing order.
|
||||||
|
tree_Node *tree_LastNode(tree_Tree *tree);
|
||||||
|
|
||||||
|
// Delete deletes an existing node from the tree.
|
||||||
|
void tree_Delete(tree_Tree *tree, tree_Node *node);
|
||||||
|
|
||||||
|
// Node_Next returns the next node. Returns NULL if the node is the last.
|
||||||
|
tree_Node *tree_Node_Next(tree_Node *node);
|
||||||
|
|
||||||
|
// Node_Previous returns the previous node. Returns NULL if first.
|
||||||
|
tree_Node *tree_Node_Previous(tree_Node *node);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
60
util/tree_internal.h
Normal file
60
util/tree_internal.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tree.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
__tree_Left,
|
||||||
|
__tree_Right,
|
||||||
|
} __tree_ConnectType;
|
||||||
|
|
||||||
|
static inline __tree_ConnectType __tree_ConnectType_Invert(__tree_ConnectType type) {
|
||||||
|
return (__tree_ConnectType)(!type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline __tree_ConnectType __tree_Tell(tree_Node *son) {
|
||||||
|
if (!son->father)
|
||||||
|
return __tree_Left;
|
||||||
|
if (son->father->left == son)
|
||||||
|
return __tree_Left;
|
||||||
|
else
|
||||||
|
return __tree_Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline tree_Node *__tree_Get(tree_Node *father, __tree_ConnectType type) {
|
||||||
|
return (type == __tree_Left) ? father->left : father->right;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void __tree_Connect(tree_Node *father, tree_Node *son, __tree_ConnectType type) {
|
||||||
|
if (son)
|
||||||
|
son->father = father;
|
||||||
|
if (father) {
|
||||||
|
if (type == __tree_Left)
|
||||||
|
father->left = son;
|
||||||
|
else
|
||||||
|
father->right = son;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotates the node up.
|
||||||
|
static inline void __tree_Rotate(tree_Node *node, tree_Node **root) {
|
||||||
|
if (!node->father)
|
||||||
|
return;
|
||||||
|
|
||||||
|
__tree_ConnectType type = __tree_Tell(node);
|
||||||
|
|
||||||
|
tree_Node *f = node->father,
|
||||||
|
*b = __tree_Get(node, __tree_ConnectType_Invert(type));
|
||||||
|
__tree_Connect(f->father, node, __tree_Tell(f));
|
||||||
|
__tree_Connect(node, f, __tree_ConnectType_Invert(type));
|
||||||
|
__tree_Connect(f, b, type);
|
||||||
|
|
||||||
|
if (!node->father)
|
||||||
|
*root = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust the tree as a Treap
|
||||||
|
static inline void __tree_treap_Adjust(tree_Node *node, tree_Node **root) {
|
||||||
|
while (node->father && node->father->internal > node->internal)
|
||||||
|
__tree_Rotate(node, root);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user