CI CodeQL License C++ Version Platform Compiler
A high-performance, modern C++17 implementation of B+ Tree data structure with comprehensive testing, memory safety, and professional documentation.
- β Complete B+ Tree Operations: Insert, Search, Delete with proper tree balancing
- β Modern C++17: Clean, type-safe implementation with proper namespacing
- β Memory Safe: RAII principles, proper destructors, and leak prevention
- β File-Based Storage: Simulates disk-based database operations
- β Comprehensive Testing: Full test suite with 8+ scenarios
- β Cross-Platform: Works on Linux, macOS, and Windows
- β Professional Structure: Industry-standard project organization
Interactive demonstration of B+ Tree operations including insertion, search, deletion, and tree visualization
- Quick Start β’ Building β’ Usage β’ API Reference β’ Testing β’ Contributing
#include <bptree/bptree.hpp> #include <iostream> int main() { // Create B+ tree with internal node limit 4, leaf node limit 3 bptree::BPTree tree(4, 3); // Insert student data FILE* file = fopen("DBFiles/101.txt", "w"); fprintf(file, "Wilson Sarah 22 89\n"); tree.insert(101, file); fclose(file); // Search for data tree.search(101); // Output: "Hurray!! Key FOUND" // Display tree structure tree.display(tree.getRoot()); return 0; }
- C++17 compatible compiler (GCC 7+, Clang 5+, MSVC 2017+)
- Make or CMake 3.15+ (optional)
This project uses GitHub Actions for automated testing across Linux, macOS, and Windows with multiple compilers.
# Clone the repository git clone <your-repo-url> cd bplus-tree # Build the project make # Run the demo ./bptree_demo # Run example program ./basic_usage # Run comprehensive tests make test
# Create build directory mkdir build && cd build # Configure and build cmake .. cmake --build . --parallel # Run tests ctest --verbose
make
- Build demo and example programsmake run-demo
- Build and run the main demomake run-example
- Build and run the basic usage examplemake test
- Run comprehensive test suitemake clean
- Clean build artifacts
Our database schema simulates a student management system:
Each record contains:
- Roll Number (Primary Key): Integer identifier
- Name: Student name
- Age: Student age
- Marks: Academic score
#include <bptree/bptree.hpp> // Create tree with custom parameters bptree::BPTree tree(4, 3); // internal_limit=4, leaf_limit=3 // Insert student record FILE* studentFile = fopen("DBFiles/12345.txt", "w"); fprintf(studentFile, "Smith Michael 21 92\n"); tree.insert(12345, studentFile); fclose(studentFile); // Search for student tree.search(12345); // Displays: "Hurray!! Key FOUND" // Delete student record tree.removeKey(12345); // Display tree structures tree.display(tree.getRoot()); // Hierarchical view tree.seqDisplay(tree.getRoot()); // Sequential leaf traversal
// Create multiple trees for different tables bptree::BPTree studentsTree(4, 3); bptree::BPTree coursesTree(6, 5); // Different capacity // Batch operations std::vector<int> rollNumbers = {101, 102, 103, 104, 105}; for (int rollNo : rollNumbers) { std::string filename = "DBFiles/" + std::to_string(rollNo) + ".txt"; FILE* file = fopen(filename.c_str(), "w"); fprintf(file, "Student_%d 21 88\n", rollNo); studentsTree.insert(rollNo, file); fclose(file); }
B+ Trees are balanced tree data structures optimized for systems that read and write large blocks of data. Unlike B-Trees, all data is stored in leaf nodes, making sequential access efficient.
- Dual Order Values: Separate limits for internal and leaf nodes
- Right-Biased Splitting: When splitting even-sized nodes, right sibling gets one extra element
- Primary Key Based: No duplicate keys allowed (maintains primary key constraints)
- Sequential Access: Leaf nodes are linked for efficient range queries
- Memory Efficient: Union-based node structure separates internal/leaf node data
class Node { bool isLeaf; std::vector<int> keys; Node* ptr2next; // Links leaf nodes for sequential access union ptr { std::vector<Node*> ptr2Tree; // Internal node children std::vector<FILE*> dataPtr; // Leaf node data pointers } ptr2TreeOrData; };
- No Parent Pointers: Avoids complexity during deletion operations
- Explicit ptr2next: Enables efficient sequential traversal
- File-Based Storage: Simulates disk block access patterns
- Union Memory Layout: Optimizes memory usage for different node types
Internal (Non-Leaf) Nodes:
ceil(maxInternalLimit/2) β€ #children β€ maxInternalLimit
ceil(maxInternalLimit/2)-1 β€ #keys β€ maxInternalLimit-1
Leaf Nodes:
ceil(maxLeafLimit/2) β€ #keys β€ maxLeafLimit
- Each key has corresponding data pointer of same size
Minimum 50% Occupancy Rule: All nodes (except root) must maintain at least 50% capacity to ensure balanced tree performance.
B+ Tree Properties B+ Tree Structure B+ Tree Constraints
The search operation efficiently navigates from root to leaf using the tree's ordered structure:
-
Internal Node Navigation:
- Find first key β₯ search key
- Follow corresponding child pointer
- If all keys < search key, follow rightmost pointer
-
Leaf Node Search:
- Perform binary search within leaf node
- Return success/failure with data location
-
Time Complexity: O(log n) for all operations
void search(int key) { Node* cursor = root; // Navigate to leaf node while (!cursor->isLeaf) { int idx = upper_bound(cursor->keys.begin(), cursor->keys.end(), key) - cursor->keys.begin(); cursor = cursor->ptr2TreeOrData.ptr2Tree[idx]; } // Search in leaf node int idx = lower_bound(cursor->keys.begin(), cursor->keys.end(), key) - cursor->keys.begin(); if (idx < cursor->keys.size() && cursor->keys[idx] == key) { // Key found - display data displayData(cursor->ptr2TreeOrData.dataPtr[idx]); } }
Our implementation uses the split-only approach for simplicity and performance:
Strategy | Approach | Pros | Cons |
---|---|---|---|
Redistribution | Try borrowing from siblings first | Better space utilization | Increased I/O operations |
Split-Only β | Immediately split full nodes | Simpler implementation, fewer I/O | Potentially more nodes |
- Navigate to Leaf: Traverse tree to find insertion point
- Check Capacity:
- If space available β Insert directly
- If full β Split node
- Handle Splits:
- Leaf Split: Copy minimum key of new node to parent
- Internal Split: Promote middle key to parent
- Propagate Upward: Repeat splitting up the tree if necessary
Inserting key 8
into the tree:
Step 1: Initial State Insertion Step 1
Step 2: Node Split Insertion Step 2
Step 3: Final State Insertion Step 3
void insert(int key, FILE* filePtr) { if (root == nullptr) { // Create root node root = new Node(); root->isLeaf = true; root->keys.push_back(key); root->ptr2TreeOrData.dataPtr.push_back(filePtr); return; } // Find leaf node for insertion Node* leaf = findLeafNode(key); if (leaf->keys.size() < maxLeafNodeLimit) { // Simple insertion insertIntoLeaf(leaf, key, filePtr); } else { // Split required splitLeafAndInsert(leaf, key, filePtr); } }
BPTree(); // Default: internal=4, leaf=3 BPTree(int degreeInternal, int degreeLeaf); // Custom configuration
void insert(int key, FILE* filePtr); // Insert key-data pair void search(int key); // Search and display data void removeKey(int key); // Delete key from tree
void display(Node* cursor); // Hierarchical tree view void seqDisplay(Node* cursor); // Sequential leaf traversal
Node* getRoot(); // Get root node int getMaxIntChildLimit(); // Get internal node capacity int getMaxLeafNodeLimit(); // Get leaf node capacity void setRoot(Node* ptr); // Set root node
class Node { public: bool isLeaf; // Node type flag std::vector<int> keys; // Sorted keys Node* ptr2next; // Next leaf node (for leaves) union ptr { std::vector<Node*> ptr2Tree; // Child pointers (internal) std::vector<FILE*> dataPtr; // Data pointers (leaf) } ptr2TreeOrData; Node(); // Constructor ~Node(); // Destructor with cleanup };
# Run comprehensive test suite make test # Or run directly ./tests/test_suite.sh # Run with verbose output ./tests/test_suite.sh --verbose # Clean build and test ./tests/test_suite.sh --clean-build
Our test suite includes 8 comprehensive scenarios covering basic operations, tree splitting, deletion, edge cases, and scalability. See Testing Workflows Guide for detailed testing instructions.
We welcome contributions! Here's how to get started:
- Fork the repository and create a feature branch
- Follow our coding standards and development setup
- Write tests for new functionality and ensure all tests pass (
make test
) - Submit a pull request with a clear description
For detailed guidelines, see CONTRIBUTING.md.
This project is licensed under the MIT License - see the LICENSE file for details.
Made with β€οΈ for the database and algorithms community
Happy Coding! π