Skip to content

Commit

Permalink
C++ backend for Self Balancing Binary Tree (#559)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kishan-Ved authored Jun 6, 2024
1 parent 557f86b commit 0bc3eb2
Show file tree
Hide file tree
Showing 6 changed files with 328 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <cstdlib>
#include <iostream>
#include <structmember.h>
#include "../../../../linear_data_structures/_backend/cpp/arrays/DynamicOneDimensionalArray.hpp"

Expand Down
9 changes: 8 additions & 1 deletion pydatastructs/trees/_backend/cpp/BinaryTreeTraversal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "../../../linear_data_structures/_backend/cpp/arrays/ArrayForTrees.hpp"
#include "BinaryTree.hpp"
#include "BinarySearchTree.hpp"
#include "SelfBalancingBinaryTree.hpp"

typedef struct {
PyObject_HEAD
Expand All @@ -32,7 +33,13 @@ static PyObject* BinaryTreeTraversal___new__(PyTypeObject* type, PyObject *args,
if (PyType_Ready(&BinarySearchTreeType) < 0) { // This has to be present to finalize a type object. This should be called on all type objects to finish their initialization.
return NULL;
}
if (PyObject_IsInstance(tree, (PyObject *)&BinarySearchTreeType)) {
if (PyType_Ready(&SelfBalancingBinaryTreeType) < 0) { // This has to be present to finalize a type object. This should be called on all type objects to finish their initialization.
return NULL;
}
if (PyObject_IsInstance(tree, (PyObject *)&SelfBalancingBinaryTreeType)) {
self->tree = reinterpret_cast<SelfBalancingBinaryTree*>(tree)->bst->binary_tree;
}
else if (PyObject_IsInstance(tree, (PyObject *)&BinarySearchTreeType)) {
self->tree = reinterpret_cast<BinarySearchTree*>(tree)->binary_tree;
}
else {
Expand Down
287 changes: 287 additions & 0 deletions pydatastructs/trees/_backend/cpp/SelfBalancingBinaryTree.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
#ifndef TREES_SELFBALANCINGSelfBalancingBinaryTree_HPP
#define TREES_SELFBALANCINGSelfBalancingBinaryTree_HPP

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <cstdlib>
#include "../../../utils/_backend/cpp/utils.hpp"
#include "../../../utils/_backend/cpp/TreeNode.hpp"
#include "../../../linear_data_structures/_backend/cpp/arrays/ArrayForTrees.hpp"
#include "../../../linear_data_structures/_backend/cpp/arrays/DynamicOneDimensionalArray.hpp"
#include "BinarySearchTree.hpp"

typedef struct {
PyObject_HEAD
BinarySearchTree* bst;
ArrayForTrees* tree;
} SelfBalancingBinaryTree;

static void SelfBalancingBinaryTree_dealloc(SelfBalancingBinaryTree *self) {
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
}

static PyObject* SelfBalancingBinaryTree___new__(PyTypeObject* type, PyObject *args, PyObject *kwds) {
SelfBalancingBinaryTree *self;
self = reinterpret_cast<SelfBalancingBinaryTree*>(type->tp_alloc(type, 0));

if (PyType_Ready(&BinarySearchTreeType) < 0) { // This has to be present to finalize a type object. This should be called on all type objects to finish their initialization.
return NULL;
}
PyObject* p = BinarySearchTree___new__(&BinarySearchTreeType, args, kwds);
self->bst = reinterpret_cast<BinarySearchTree*>(p);
self->tree = reinterpret_cast<BinarySearchTree*>(p)->binary_tree->tree;

return reinterpret_cast<PyObject*>(self);
}

static PyObject* SelfBalancingBinaryTree___str__(SelfBalancingBinaryTree *self) {
return BinarySearchTree___str__(self->bst);
}

static PyObject* SelfBalancingBinaryTree_insert(SelfBalancingBinaryTree* self, PyObject* args) {
return BinarySearchTree_insert(self->bst, args);
}

static PyObject* SelfBalancingBinaryTree_search(SelfBalancingBinaryTree* self, PyObject *args, PyObject *kwds) {
return BinarySearchTree_search(self->bst, args, kwds);
}

static PyObject* SelfBalancingBinaryTree_delete(SelfBalancingBinaryTree* self, PyObject *args, PyObject *kwds) {
return BinarySearchTree_delete(self->bst, args, kwds);
}

static PyObject* SelfBalancingBinaryTree_lower_bound(SelfBalancingBinaryTree* self, PyObject *args, PyObject *kwds) {
return BinarySearchTree_lower_bound(self->bst, args, kwds);
}

static PyObject* SelfBalancingBinaryTree_upper_bound(SelfBalancingBinaryTree* self, PyObject *args, PyObject *kwds) {
return BinarySearchTree_upper_bound(self->bst, args, kwds);
}

static PyObject* SelfBalancingBinaryTree__simple_path(SelfBalancingBinaryTree* self, PyObject *args) {
return BinarySearchTree__simple_path(self->bst, args);
}

static PyObject* SelfBalancingBinaryTree__lca_1(SelfBalancingBinaryTree* self, PyObject *args) {
return BinarySearchTree__simple_path(self->bst, args);
}

static PyObject* SelfBalancingBinaryTree__lca_2(SelfBalancingBinaryTree* self, PyObject *args) {
return BinarySearchTree__simple_path(self->bst, args);
}

static PyObject* SelfBalancingBinaryTree_lowest_common_ancestor(SelfBalancingBinaryTree* self, PyObject *args) {
return BinarySearchTree_lowest_common_ancestor(self->bst, args);
}

static PyObject* SelfBalancingBinaryTree_rank(SelfBalancingBinaryTree* self, PyObject *args) {
return BinarySearchTree_rank(self->bst, args);
}

static PyObject* SelfBalancingBinaryTree_select(SelfBalancingBinaryTree* self, PyObject *args) {
return BinarySearchTree_select(self->bst, args);
}

static PyObject* SelfBalancingBinaryTree__right_rotate(SelfBalancingBinaryTree* self, PyObject *args) {
PyObject* j = PyObject_GetItem(args, PyZero);
PyObject* k = PyObject_GetItem(args, PyOne);
BinaryTree* bt = self->bst->binary_tree;
PyObject* y = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->right;
if (y != Py_None) {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(y)])->parent = j;
}
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(j)])->left = y;
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(j)])->parent;
if (reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent != Py_None) {
if (reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent)])->left == j) {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent)])->left = k;
}
else {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent)])->right = k;
}
}
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(j)])->parent = k;
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->right = j;
PyObject* kp = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent;
if (kp == Py_None) {
bt->root_idx = k;
}
Py_RETURN_NONE;
}

static PyObject* SelfBalancingBinaryTree__left_rotate(SelfBalancingBinaryTree* self, PyObject *args) {
PyObject* j = PyObject_GetItem(args, PyZero);
PyObject* k = PyObject_GetItem(args, PyOne);
BinaryTree* bt = self->bst->binary_tree;
PyObject* y = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->left;
if (y != Py_None) {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(y)])->parent = j;
}
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(j)])->right = y;
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(j)])->parent;
if (reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent != Py_None) {
if (reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent)])->left == j) {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent)])->left = k;
}
else {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent)])->right = k;
}
}
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(j)])->parent = k;
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->left = j;
PyObject* kp = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent;
if (kp == Py_None) {
bt->root_idx = k;
}
Py_RETURN_NONE;
}

static PyObject* SelfBalancingBinaryTree__left_right_rotate(SelfBalancingBinaryTree* self, PyObject *args) {
PyObject* j = PyObject_GetItem(args, PyZero);
PyObject* k = PyObject_GetItem(args, PyOne);
BinaryTree* bt = self->bst->binary_tree;

PyObject* i = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->right;
PyObject* v = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(i)])->left;
PyObject* w = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(i)])->right;

reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->right = v;
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(j)])->left = w;

if (v != Py_None) {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(v)])->parent = k;
}
if (w != Py_None) {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(w)])->parent = j;
}

reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(i)])->left = k;
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(i)])->right = j;
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(i)])->parent = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(j)])->parent;

reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent = i;
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(j)])->parent = i;

PyObject* ip = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(i)])->parent;
if (ip != Py_None) {
if (reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(ip)])->left == j) {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(ip)])->left = i;
}
else {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(ip)])->right = i;
}
}
else {
bt->root_idx = i;
}
Py_RETURN_NONE;
}

static PyObject* SelfBalancingBinaryTree__right_left_rotate(SelfBalancingBinaryTree* self, PyObject *args) {
PyObject* j = PyObject_GetItem(args, PyZero);
PyObject* k = PyObject_GetItem(args, PyOne);
BinaryTree* bt = self->bst->binary_tree;

PyObject* i = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->left;
PyObject* v = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(i)])->left;
PyObject* w = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(i)])->right;

reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->left = w;
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(j)])->right = v;

if (v != Py_None) {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(v)])->parent = j;
}
if (w != Py_None) {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(w)])->parent = k;
}

reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(i)])->right = k;
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(i)])->left = j;
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(i)])->parent = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(j)])->parent;

reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(k)])->parent = i;
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(j)])->parent = i;

PyObject* ip = reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(i)])->parent;
if (ip != Py_None) {
if (reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(ip)])->left == j) {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(ip)])->left = i;
}
else {
reinterpret_cast<TreeNode*>(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(ip)])->right = i;
}
}
else {
bt->root_idx = i;
}
Py_RETURN_NONE;
}

static struct PyMethodDef SelfBalancingBinaryTree_PyMethodDef[] = {
{"insert", (PyCFunction) SelfBalancingBinaryTree_insert, METH_VARARGS | METH_KEYWORDS, NULL},
{"delete", (PyCFunction) SelfBalancingBinaryTree_delete, METH_VARARGS | METH_KEYWORDS, NULL},
{"search", (PyCFunction) SelfBalancingBinaryTree_search, METH_VARARGS | METH_KEYWORDS, NULL},
{"lower_bound", (PyCFunction) SelfBalancingBinaryTree_lower_bound, METH_VARARGS | METH_KEYWORDS, NULL},
{"upper_bound", (PyCFunction) SelfBalancingBinaryTree_upper_bound, METH_VARARGS | METH_KEYWORDS, NULL},
{"_simple_path", (PyCFunction) SelfBalancingBinaryTree__simple_path, METH_VARARGS, NULL},
{"_lca_1", (PyCFunction) SelfBalancingBinaryTree__lca_1, METH_VARARGS, NULL},
{"_lca_2", (PyCFunction) SelfBalancingBinaryTree__lca_2, METH_VARARGS, NULL},
{"lowest_common_ancestor", (PyCFunction) SelfBalancingBinaryTree_lowest_common_ancestor, METH_VARARGS, NULL},
{"rank", (PyCFunction) SelfBalancingBinaryTree_rank, METH_VARARGS, NULL},
{"select", (PyCFunction) SelfBalancingBinaryTree_select, METH_VARARGS, NULL},
{"_right_rotate", (PyCFunction) SelfBalancingBinaryTree__right_rotate, METH_VARARGS, NULL},
{"_left_rotate", (PyCFunction) SelfBalancingBinaryTree__left_rotate, METH_VARARGS, NULL},
{"_left_right_rotate", (PyCFunction) SelfBalancingBinaryTree__left_right_rotate, METH_VARARGS, NULL},
{"_right_left_rotate", (PyCFunction) SelfBalancingBinaryTree__right_left_rotate, METH_VARARGS, NULL},
{NULL}
};

static PyMemberDef SelfBalancingBinaryTree_PyMemberDef[] = {
{"tree", T_OBJECT_EX, offsetof(SelfBalancingBinaryTree, tree), 0, "tree"},
{NULL} /* Sentinel */
};


static PyTypeObject SelfBalancingBinaryTreeType = {
/* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "SelfBalancingBinaryTree",
/* tp_basicsize */ sizeof(SelfBalancingBinaryTree),
/* tp_itemsize */ 0,
/* tp_dealloc */ (destructor) SelfBalancingBinaryTree_dealloc,
/* tp_print */ 0,
/* tp_getattr */ 0,
/* tp_setattr */ 0,
/* tp_reserved */ 0,
/* tp_repr */ 0,
/* tp_as_number */ 0,
/* tp_as_sequence */ 0,
/* tp_as_mapping */ 0,
/* tp_hash */ 0,
/* tp_call */ 0,
/* tp_str */ (reprfunc) SelfBalancingBinaryTree___str__,
/* tp_getattro */ 0,
/* tp_setattro */ 0,
/* tp_as_buffer */ 0,
/* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
/* tp_doc */ 0,
/* tp_traverse */ 0,
/* tp_clear */ 0,
/* tp_richcompare */ 0,
/* tp_weaklistoffset */ 0,
/* tp_iter */ 0,
/* tp_iternext */ 0,
/* tp_methods */ SelfBalancingBinaryTree_PyMethodDef,
/* tp_members */ SelfBalancingBinaryTree_PyMemberDef,
/* tp_getset */ 0,
/* tp_base */ &BinarySearchTreeType,
/* tp_dict */ 0,
/* tp_descr_get */ 0,
/* tp_descr_set */ 0,
/* tp_dictoffset */ 0,
/* tp_init */ 0,
/* tp_alloc */ 0,
/* tp_new */ SelfBalancingBinaryTree___new__,
};

#endif
7 changes: 7 additions & 0 deletions pydatastructs/trees/_backend/cpp/trees.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "BinaryTree.hpp"
#include "BinarySearchTree.hpp"
#include "BinaryTreeTraversal.hpp"
#include "SelfBalancingBinaryTree.hpp"

static struct PyModuleDef trees_struct = {
PyModuleDef_HEAD_INIT,
Expand Down Expand Up @@ -33,5 +34,11 @@ PyMODINIT_FUNC PyInit__trees(void) {
Py_INCREF(&BinaryTreeTraversalType);
PyModule_AddObject(trees, "BinaryTreeTraversal", reinterpret_cast<PyObject*>(&BinaryTreeTraversalType));

if (PyType_Ready(&SelfBalancingBinaryTreeType) < 0) {
return NULL;
}
Py_INCREF(&SelfBalancingBinaryTreeType);
PyModule_AddObject(trees, "SelfBalancingBinaryTree", reinterpret_cast<PyObject*>(&SelfBalancingBinaryTreeType));

return trees;
}
9 changes: 9 additions & 0 deletions pydatastructs/trees/binary_trees.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,15 @@ class SelfBalancingBinaryTree(BinarySearchTree):
"""
Represents Base class for all rotation based balancing trees like AVL tree, Red Black tree, Splay Tree.
"""
def __new__(cls, key=None, root_data=None, comp=None,
is_order_statistic=False, **kwargs):
backend = kwargs.get('backend', Backend.PYTHON)
if backend == Backend.CPP:
if comp is None:
comp = lambda key1, key2: key1 < key2
return _trees.SelfBalancingBinaryTree(key, root_data, comp, is_order_statistic, **kwargs) # If any argument is not given, then it is passed as None, except for comp
return super().__new__(cls, key, root_data, comp, is_order_statistic, **kwargs)

def _right_rotate(self, j, k):
y = self.tree[k].right
if y is not None:
Expand Down
Loading

0 comments on commit 0bc3eb2

Please sign in to comment.