Files
letta-server/tests/data/data_structures.cpp
2025-10-07 17:50:45 -07:00

286 lines
7.2 KiB
C++

#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
#include <stdexcept>
/**
* Binary Search Tree implementation with smart pointers
* Template class supporting any comparable type
*/
template<typename T>
class BinarySearchTree {
private:
struct Node {
T data;
std::unique_ptr<Node> left;
std::unique_ptr<Node> right;
Node(const T& value) : data(value), left(nullptr), right(nullptr) {}
};
std::unique_ptr<Node> root;
size_t size_;
void insertHelper(std::unique_ptr<Node>& node, const T& value) {
if (!node) {
node = std::make_unique<Node>(value);
++size_;
return;
}
if (value < node->data) {
insertHelper(node->left, value);
} else if (value > node->data) {
insertHelper(node->right, value);
}
// Ignore duplicates
}
bool searchHelper(const std::unique_ptr<Node>& node, const T& value) const {
if (!node) return false;
if (value == node->data) return true;
else if (value < node->data) return searchHelper(node->left, value);
else return searchHelper(node->right, value);
}
void inorderHelper(const std::unique_ptr<Node>& node, std::vector<T>& result) const {
if (!node) return;
inorderHelper(node->left, result);
result.push_back(node->data);
inorderHelper(node->right, result);
}
std::unique_ptr<Node> removeHelper(std::unique_ptr<Node> node, const T& value) {
if (!node) return nullptr;
if (value < node->data) {
node->left = removeHelper(std::move(node->left), value);
} else if (value > node->data) {
node->right = removeHelper(std::move(node->right), value);
} else {
// Node to delete found
--size_;
if (!node->left) return std::move(node->right);
if (!node->right) return std::move(node->left);
// Node has two children
Node* successor = findMin(node->right.get());
node->data = successor->data;
node->right = removeHelper(std::move(node->right), successor->data);
++size_; // Compensate for decrement in recursive call
}
return node;
}
Node* findMin(Node* node) const {
while (node->left) {
node = node->left.get();
}
return node;
}
public:
BinarySearchTree() : root(nullptr), size_(0) {}
void insert(const T& value) {
insertHelper(root, value);
}
bool search(const T& value) const {
return searchHelper(root, value);
}
void remove(const T& value) {
root = removeHelper(std::move(root), value);
}
std::vector<T> inorderTraversal() const {
std::vector<T> result;
inorderHelper(root, result);
return result;
}
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
void clear() {
root.reset();
size_ = 0;
}
};
/**
* Dynamic Array implementation with automatic resizing
*/
template<typename T>
class DynamicArray {
private:
std::unique_ptr<T[]> data;
size_t capacity_;
size_t size_;
void resize() {
size_t newCapacity = capacity_ == 0 ? 1 : capacity_ * 2;
auto newData = std::make_unique<T[]>(newCapacity);
for (size_t i = 0; i < size_; ++i) {
newData[i] = std::move(data[i]);
}
data = std::move(newData);
capacity_ = newCapacity;
}
public:
DynamicArray() : data(nullptr), capacity_(0), size_(0) {}
explicit DynamicArray(size_t initialCapacity)
: data(std::make_unique<T[]>(initialCapacity)),
capacity_(initialCapacity),
size_(0) {}
void pushBack(const T& value) {
if (size_ >= capacity_) {
resize();
}
data[size_++] = value;
}
void pushBack(T&& value) {
if (size_ >= capacity_) {
resize();
}
data[size_++] = std::move(value);
}
T& operator[](size_t index) {
if (index >= size_) {
throw std::out_of_range("Index out of bounds");
}
return data[index];
}
const T& operator[](size_t index) const {
if (index >= size_) {
throw std::out_of_range("Index out of bounds");
}
return data[index];
}
void popBack() {
if (size_ > 0) {
--size_;
}
}
size_t size() const { return size_; }
size_t capacity() const { return capacity_; }
bool empty() const { return size_ == 0; }
void clear() { size_ = 0; }
// Iterator support
T* begin() { return data.get(); }
T* end() { return data.get() + size_; }
const T* begin() const { return data.get(); }
const T* end() const { return data.get() + size_; }
};
/**
* Stack implementation using dynamic array
*/
template<typename T>
class Stack {
private:
DynamicArray<T> container;
public:
void push(const T& value) {
container.pushBack(value);
}
void push(T&& value) {
container.pushBack(std::move(value));
}
void pop() {
if (empty()) {
throw std::runtime_error("Stack underflow");
}
container.popBack();
}
T& top() {
if (empty()) {
throw std::runtime_error("Stack is empty");
}
return container[container.size() - 1];
}
const T& top() const {
if (empty()) {
throw std::runtime_error("Stack is empty");
}
return container[container.size() - 1];
}
bool empty() const { return container.empty(); }
size_t size() const { return container.size(); }
};
// Demonstration and testing
int main() {
std::cout << "=== Binary Search Tree Demo ===" << std::endl;
BinarySearchTree<int> bst;
std::vector<int> values = {50, 30, 70, 20, 40, 60, 80, 10, 25, 35};
for (int val : values) {
bst.insert(val);
}
std::cout << "Tree size: " << bst.size() << std::endl;
std::cout << "Inorder traversal: ";
auto inorder = bst.inorderTraversal();
for (size_t i = 0; i < inorder.size(); ++i) {
std::cout << inorder[i];
if (i < inorder.size() - 1) std::cout << ", ";
}
std::cout << std::endl;
std::cout << "\n=== Dynamic Array Demo ===" << std::endl;
DynamicArray<std::string> arr;
arr.pushBack("Hello");
arr.pushBack("World");
arr.pushBack("C++");
arr.pushBack("Templates");
std::cout << "Array contents: ";
for (size_t i = 0; i < arr.size(); ++i) {
std::cout << arr[i];
if (i < arr.size() - 1) std::cout << ", ";
}
std::cout << std::endl;
std::cout << "\n=== Stack Demo ===" << std::endl;
Stack<int> stack;
for (int i = 1; i <= 5; ++i) {
stack.push(i * 10);
}
std::cout << "Stack contents (top to bottom): ";
while (!stack.empty()) {
std::cout << stack.top() << " ";
stack.pop();
}
std::cout << std::endl;
return 0;
}