gotreap

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2025 License: CC0-1.0 Imports: 4 Imported by: 0

README

🌳 gotreap

Go Reference Go Report Card

A complete, type-safe Treap (Tree + Heap) data structure implementation in Go with generics.

Gotreap provides a self-balancing binary search tree that combines the properties of a binary search tree and a max-heap. Perfect for scenarios requiring fast ordered operations, range queries, and efficient splits/merges.


📋 Table of Contents


🤔 What is a Treap?

A Treap is a randomized balanced binary search tree that maintains two properties simultaneously:

  1. Binary Search Tree (BST) property: In-order traversal yields sorted elements
  2. Max-Heap property: Each node has a random priority higher than its children

This dual property ensures O(log n) expected time for insertions, deletions, and searches without requiring complex rotation logic like AVL or Red-Black trees.

Why Choose Treap?
  • Simple Implementation - Easier to understand and debug than AVL/Red-Black trees
  • Self-Balancing - Automatic balancing via random priorities
  • Fast Split/Merge - O(log n) tree splitting and merging operations
  • Order Statistics - Direct index-based access to the k-th element
  • Range Operations - Efficient range queries and bulk deletions
  • Type-Safe - Full Go generics support for any comparable type

✨ Features

  • 🎯 Type-safe generics for any ordered or custom-comparable types
  • 🔍 Fast lookups with O(log n) search, insert, and delete
  • 📊 Index-based access - Get element by position like an array
  • 🎲 Randomized balancing - No manual rebalancing needed
  • Split & Merge - Divide and combine treaps efficiently
  • 🔢 Range operations - Count, erase, and query ranges
  • 🔄 Iterator support - Forward and backward iteration with Go 1.23+ iter.Seq
  • 🧭 Bidirectional navigation - Next, Prev, Jump operations on nodes
  • 📦 Zero dependencies - Only uses Go standard library
  • 🧪 Well-tested - Comprehensive test suite

📦 Installation

go get github.com/strokovok/gotreap

Requirements: Go 1.23 or higher (uses generics and iter package)


🚀 Quick Start

package main

import (
    "fmt"
    "github.com/strokovok/gotreap"
)

func main() {
    // Create a treap with integers (automatically sorted)
    treap := gotreap.NewAutoOrderTreap(5, 2, 8, 1, 9, 3)

    // Insert elements
    treap.InsertRight(7)
    treap.InsertLeft(4)

    // Check size
    fmt.Println("Size:", treap.Size()) // Output: 8

    // Access by index (0-based, like a sorted array)
    fmt.Println("Element at index 3:", treap.At(3).Value()) // Output: 4

    // Find minimum and maximum
    fmt.Println("Min:", treap.Leftmost().Value())  // Output: 1
    fmt.Println("Max:", treap.Rightmost().Value()) // Output: 9

    // Iterate over all values in order
    for value := range treap.Values() {
        fmt.Println(value)
    }
}

🔧 Core Operations

Creating a Treap
// Auto-ordered treap for built-in comparable types
treap := gotreap.NewAutoOrderTreap(3, 1, 4, 1, 5)

// Custom comparison function
reverseTreap := gotreap.NewTreap(
    func(a, b int) bool { return a > b }, // Descending order
    3, 1, 4, 1, 5,
)

// Custom type with custom comparator
type Person struct {
    Name string
    Age  int
}

byAge := gotreap.NewTreap(
    func(a, b Person) bool { return a.Age < b.Age },
    Person{"Alice", 30},
    Person{"Bob", 25},
)
Insertion
// Insert before duplicates
index := treap.InsertLeft(42)

// Insert after duplicates
index := treap.InsertRight(42)
Deletion
// Remove all occurrences of a value
count := treap.EraseAll(42)

// Remove first n occurrences
count := treap.EraseLeftmost(42, 3)

// Remove by index
count := treap.EraseAt(5, 2) // Remove 2 elements starting at index 5

// Remove by range
count := treap.EraseRange(10, true, 20, false) // Remove [10, 20)
// Access by index (supports negative indexing)
node := treap.At(0)    // First element
node := treap.At(-1)   // Last element

// Find bounds
node, idx := treap.FindLowerBound(42) // First element >= 42
node, idx := treap.FindUpperBound(42) // Last element <= 42

// Check existence
count := treap.Count(42)
exists := count > 0

🎓 Advanced Usage

Range Queries
// Count elements in range [10, 20]
count := treap.CountRange(10, true, 20, true)

// Count elements in range (10, 20)
count := treap.CountRange(10, false, 20, false)

// Erase range [15, 25)
erased := treap.EraseRange(15, true, 25, false)
Splitting & Merging
// Split before value
left, right := treap.SplitBefore(50)  // left: < 50, right: >= 50

// Split after value
left, right := treap.SplitAfter(50)   // left: <= 50, right: > 50

// Split by index
left, right := treap.Cut(5)           // First 5 elements vs rest

// Negative index cuts from end
left, right := treap.Cut(-3)          // All but last 3 vs last 3

// Merge two treaps (must have same ordering function)
merged := gotreap.Merge(left, right)
Navigation & Iteration
// Get extrema and pop
min := treap.Leftmost()
max := treap.Rightmost()

value, ok := treap.PopLeftmost()
value, ok := treap.PopRightmost()

// Node navigation
node := treap.At(5)
next := node.Next()
prev := node.Prev()
jumped := node.JumpRight(10)  // Jump 10 positions right
jumped := node.JumpLeft(5)    // Jump 5 positions left

// Get node index
idx := node.Index()

// Iterate forward
for node := range treap.Elements() {
    fmt.Println(node.Value())
}

// Iterate backward
for value := range treap.ValuesBackwards() {
    fmt.Println(value)
}

📚 API Reference

Constructor Functions
Function Description
NewAutoOrderTreap[T cmp.Ordered](values ...T) Create treap with natural ordering
NewAutoOrderTreapWithRand[T cmp.Ordered](randFn, values) Create treap with custom RNG
NewTreap[T any](lessFn, values) Create treap with custom comparator
NewTreapWithRand[T any](lessFn, randFn, values) Full control over ordering and RNG
Insertion Methods
Method Time Description
InsertLeft(value) O(log n) Insert before equal elements
InsertRight(value) O(log n) Insert after equal elements
Deletion Methods
Method Time Description
EraseAll(value) O(log n) Remove all occurrences
EraseLeftmost(value, n) O(log n) Remove first n occurrences
EraseRightmost(value, n) O(log n) Remove last n occurrences
EraseAt(index, count) O(log n) Remove count elements at index
EraseRange(start, inclStart, end, inclEnd) O(log n) Remove elements in range
Clear() O(1) Remove all elements
Access Methods
Method Time Description
At(index) O(log n) Get element at index (supports negative)
Leftmost() O(log n) Get minimum element
Rightmost() O(log n) Get maximum element
Root() O(1) Get arbitrary node
Search Methods
Method Time Description
FindLowerBound(value) O(log n) First element >= value
FindUpperBound(value) O(log n) Last element <= value
Count(value) O(log n) Count occurrences
CountRange(start, inclStart, end, inclEnd) O(log n) Count elements in range
Split & Merge
Method Time Description
SplitBefore(value) O(log n) Split at first element >= value
SplitAfter(value) O(log n) Split after last element <= value
Cut(n) O(log n) Split at index n
Merge(left, right) O(log n) Combine two treaps
Utility Methods
Method Time Description
Size() O(1) Number of elements
Empty() O(1) Check if empty
PopLeftmost() O(log n) Remove and return minimum
PopRightmost() O(log n) Remove and return maximum
Iteration
Method Description
Elements() Iterate nodes left-to-right
ElementsBackwards() Iterate nodes right-to-left
Values() Iterate values left-to-right
ValuesBackwards() Iterate values right-to-left
Node Methods
Method Time Description
Value() O(1) Get node's value
Index() O(log n) Get node's position (-1 if nil)
Next() O(log n) Get next node in order
Prev() O(log n) Get previous node in order
JumpRight(n) O(log n) Jump n positions right
JumpLeft(n) O(log n) Jump n positions left
Leftmost() O(log n) Get minimum from this node's tree
Rightmost() O(log n) Get maximum from this node's tree
Valid() O(1) Check if node is non-nil

⚡ Performance

All operations have O(log n) expected time complexity:

Operation Complexity Notes
Insert O(log n) Amortized due to randomization
Delete O(log n) Including range deletions
Search O(log n) Binary search on BST property
Access by index O(log n) Via augmented size information
Split O(log n) Efficient partition operation
Merge O(log n) Combine two treaps
Range query O(log n) Count or access ranges

Space Complexity: O(n) where n is the number of elements.

When to Use Treap vs Other Structures
Use Case Treap Alternatives
Frequent splits/merges ✅ Excellent ❌ Slow in most BSTs
Order statistics (k-th element) ✅ O(log n) ⚠️ O(n) in standard BSTs
Range operations ✅ Fast ⚠️ Varies
Simple implementation ✅ Yes ❌ AVL/RB complex
Predictable worst-case ⚠️ Randomized ✅ AVL/RB guaranteed
Memory efficiency ⚠️ Parent pointers ✅ Some BSTs

💡 Examples

Priority Queue with Order Statistics
// Min-heap style priority queue that also supports "get k-th smallest"
pq := gotreap.NewAutoOrderTreap[int]()

pq.InsertRight(5)
pq.InsertRight(2)
pq.InsertRight(8)
pq.InsertRight(1)

// Get minimum (like heap.Pop)
min, _ := pq.PopLeftmost()  // 1

// Get 2nd smallest element (not possible with standard heap!)
secondSmallest := pq.At(1).Value()  // 2
Leaderboard System
type Player struct {
    Name  string
    Score int
}

// Descending order by score
leaderboard := gotreap.NewTreap(
    func(a, b Player) bool { return a.Score > b.Score },
)

leaderboard.InsertRight(Player{"Alice", 1000})
leaderboard.InsertRight(Player{"Bob", 850})
leaderboard.InsertRight(Player{"Charlie", 1200})

// Top 3 players
for i := 0; i < 3 && i < leaderboard.Size(); i++ {
    player := leaderboard.At(i).Value()
    fmt.Printf("%d. %s - %d points\n", i+1, player.Name, player.Score)
}

// Find player rank
bobNode, rank := leaderboard.FindLowerBound(Player{"", 850})
fmt.Printf("Bob is rank %d\n", rank+1)
Time-Based Event Log
type Event struct {
    Timestamp time.Time
    Message   string
}

events := gotreap.NewTreap(
    func(a, b Event) bool { return a.Timestamp.Before(b.Timestamp) },
)

// Add events
events.InsertRight(Event{time.Now(), "Server started"})
events.InsertRight(Event{time.Now().Add(5 * time.Minute), "User logged in"})

// Query events in time range
start := time.Now().Add(-1 * time.Hour)
end := time.Now()

startEvent := Event{Timestamp: start}
endEvent := Event{Timestamp: end}

count := events.CountRange(startEvent, true, endEvent, true)
fmt.Printf("Events in last hour: %d\n", count)
Interval Merging
type Interval struct {
    Start, End int
}

intervals := gotreap.NewTreap(
    func(a, b Interval) bool { return a.Start < b.Start },
    Interval{1, 3},
    Interval{2, 6},
    Interval{8, 10},
    Interval{15, 18},
)

// Process intervals by iterating
prev := intervals.Leftmost()
for node := prev.Next(); node != nil; node = node.Next() {
    if prev.Value().End >= node.Value().Start {
        // Overlapping intervals - merge logic here
        fmt.Printf("Merge [%d,%d] and [%d,%d]\n",
            prev.Value().Start, prev.Value().End,
            node.Value().Start, node.Value().End)
    }
    prev = node
}

🧪 Testing

Run the test suite:

go test -v

All operations are thoroughly tested with:

  • ✅ Edge cases (empty treaps, single elements)
  • ✅ Boundary conditions (negative indices, out-of-bounds)
  • ✅ Stress tests (1000+ operations)
  • ✅ Parent pointer integrity
  • ✅ BST and heap invariants
  • ✅ Panic conditions

🤝 Contributing

Contributions are welcome! Here's how you can help:

  1. 🐛 Report bugs - Open an issue with reproduction steps
  2. 💡 Suggest features - Share your ideas for improvements
  3. 📝 Improve docs - Fix typos or add examples
  4. 🔧 Submit PRs - Follow the existing code style
Development Guidelines
  • Write tests for new features
  • Maintain backward compatibility
  • Update documentation
  • Run go fmt and go vet
  • Ensure all tests pass

📄 License

CC0-1.0 License - see LICENSE file for details.


🙏 Acknowledgments

Based on the classic Treap data structure introduced by Cecilia Aragon and Raimund Seidel (1989).


📞 Support


Made with ❤️ for the Go community

Star ⭐ this repository if you find it useful!

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Node

type Node[T any] struct {
	// contains filtered or unexported fields
}

func (*Node[T]) Index

func (t *Node[T]) Index() int

Index computes the zero-based position of t within an in-order traversal. Returns -1 if t is nil.

func (*Node[T]) JumpLeft

func (t *Node[T]) JumpLeft(n int) *Node[T]

JumpLeft will return element that is n positions to the left, or -n positions to the right if n is negative. If there's no such element, nil will be returned.

func (*Node[T]) JumpRight

func (t *Node[T]) JumpRight(n int) *Node[T]

JumpRight will return element that is n positions to the right, or -n positions to the left n is negative. If there's no such element, nil will be returned.

func (*Node[T]) Leftmost

func (t *Node[T]) Leftmost() *Node[T]

Leftmost returns the minimum node in the treap containing t.

func (*Node[T]) Next

func (t *Node[T]) Next() *Node[T]

Next returns the in-order successor of t within the treap.

func (*Node[T]) Prev

func (t *Node[T]) Prev() *Node[T]

Prev returns the in-order predecessor of t within the treap.

func (*Node[T]) Rightmost

func (t *Node[T]) Rightmost() *Node[T]

Rightmost returns the maximum node in the treap containing t.

func (*Node[T]) Valid

func (t *Node[T]) Valid() bool

Valid reports whether t references an actual node.

func (*Node[T]) Value

func (t *Node[T]) Value() (result T)

Value returns the stored node value or the zero value if t is nil.

type Treap

type Treap[T any] struct {
	// contains filtered or unexported fields
}

func Merge

func Merge[T any](left *Treap[T], right *Treap[T]) *Treap[T]

Merge joins two treaps that share the same ordering function. The treaps must use equivalent lessFn comparators, otherwise the resulting treap will have undefined behavior. Both treaps are consumed.

func NewAutoOrderTreap

func NewAutoOrderTreap[T cmp.Ordered](values ...T) *Treap[T]

NewAutoOrderTreap builds an ordered treap using the natural ordering for type T.

func NewAutoOrderTreapWithRand

func NewAutoOrderTreapWithRand[T cmp.Ordered](randFn func() int, values ...T) *Treap[T]

NewAutoOrderTreapWithRand builds an ordered treap using the natural ordering for type T and a custom random function.

func NewTreap

func NewTreap[T any](lessFn func(a T, b T) bool, values ...T) *Treap[T]

NewTreap constructs a treap using lessFn for ordering and optionally inserts values.

func NewTreapWithRand

func NewTreapWithRand[T any](lessFn func(a T, b T) bool, randFn func() int, values ...T) *Treap[T]

NewTreapWithRand constructs a treap using lessFn for ordering, randFn for tree balancing, and optionally inserts values.

func (*Treap[T]) At

func (t *Treap[T]) At(index int) *Node[T]

At returns the node located at the provided index or nil if it is out of range.

func (*Treap[T]) Clear

func (t *Treap[T]) Clear()

Clear removes all elements from the treap.

func (*Treap[T]) Count

func (t *Treap[T]) Count(value T) int

Count reports the number of occurrences of value in the treap.

func (*Treap[T]) CountRange

func (t *Treap[T]) CountRange(startValue T, inclusiveStart bool, endValue T, inclusiveEnd bool) int

CountRange returns how many values fall between startValue and endValue. Each bound contributes to the count only when its inclusive flag is true, so exclusive flags treat that bound as open. Panics if endValue < startValue, or if startValue == endValue with non-inclusive bounds.

func (*Treap[T]) Cut

func (t *Treap[T]) Cut(n int) (left *Treap[T], right *Treap[T])

Cut splits the treap into the first n elements and the remainder. If n is negative, cuts from the end (e.g., Cut(-2) returns all but the last 2 elements as left). If the computed position is negative, everything goes to right.

func (*Treap[T]) Elements

func (t *Treap[T]) Elements() iter.Seq[*Node[T]]

Iterate over treap elements (leftmost to rightmost)

func (*Treap[T]) ElementsBackwards

func (t *Treap[T]) ElementsBackwards() iter.Seq[*Node[T]]

Iterate over treap elements in reverse order (rightmost to leftmost)

func (*Treap[T]) Empty

func (t *Treap[T]) Empty() bool

Empty reports whether the treap contains no elements.

func (*Treap[T]) EraseAll

func (t *Treap[T]) EraseAll(value T) (erasedCount int)

EraseAll removes every occurrence of value and reports how many were deleted.

func (*Treap[T]) EraseAt

func (t *Treap[T]) EraseAt(index int, count int) (erasedCount int)

EraseAt removes up to count elements starting at index and returns how many were erased. Supports negative indexing where -1 refers to the last element. Panics if count is negative.

func (*Treap[T]) EraseLeftmost

func (t *Treap[T]) EraseLeftmost(value T, n int) (erasedCount int)

EraseLeftmost removes up to n matching values starting from the leftmost occurrence.

func (*Treap[T]) EraseRange

func (t *Treap[T]) EraseRange(startValue T, inclusiveStart bool, endValue T, inclusiveEnd bool) (erasedCount int)

EraseRange removes values between startValue and endValue. Each bound is removed only when its corresponding inclusive flag is true, and the method reports how many values were erased. Panics if endValue < startValue, or if startValue == endValue with non-inclusive bounds.

func (*Treap[T]) EraseRightmost

func (t *Treap[T]) EraseRightmost(value T, n int) (erasedCount int)

EraseRightmost removes up to n matching values starting from the rightmost occurrence.

func (*Treap[T]) FindLowerBound

func (t *Treap[T]) FindLowerBound(value T) (node *Node[T], index int)

FindLowerBound returns the first node not less than value along with its index.

func (*Treap[T]) FindUpperBound

func (t *Treap[T]) FindUpperBound(value T) (node *Node[T], index int)

FindUpperBound returns the last node not greater than value along with its index.

func (*Treap[T]) InsertLeft

func (t *Treap[T]) InsertLeft(value T) (index int)

InsertLeft inserts value before any equal elements and returns its index.

func (*Treap[T]) InsertRight

func (t *Treap[T]) InsertRight(value T) (index int)

InsertRight inserts value after any equal elements and returns its index.

func (*Treap[T]) Leftmost

func (t *Treap[T]) Leftmost() *Node[T]

Leftmost returns the minimum node stored in the treap.

func (*Treap[T]) PopLeftmost

func (t *Treap[T]) PopLeftmost() (value T, ok bool)

PopLeftmost removes and returns the minimum value, reporting success.

func (*Treap[T]) PopRightmost

func (t *Treap[T]) PopRightmost() (value T, ok bool)

PopRightmost removes and returns the maximum value, reporting success.

func (*Treap[T]) Rightmost

func (t *Treap[T]) Rightmost() *Node[T]

Rightmost returns the maximum node stored in the treap.

func (*Treap[T]) Root

func (t *Treap[T]) Root() *Node[T]

Root returns the internal root node of the treap. Returns nil if the tree is empty. Note: The root node is arbitrary - prefer At(0) for first element, Leftmost() for minimum, or Rightmost() for maximum.

func (*Treap[T]) Size

func (t *Treap[T]) Size() int

Size reports the number of elements stored in the treap.

func (*Treap[T]) SplitAfter

func (t *Treap[T]) SplitAfter(value T) (left *Treap[T], right *Treap[T])

SplitAfter splits the treap after the last value less than or equal to value.

func (*Treap[T]) SplitBefore

func (t *Treap[T]) SplitBefore(value T) (left *Treap[T], right *Treap[T])

SplitBefore splits the treap at the first value not less than value.

func (*Treap[T]) Values

func (t *Treap[T]) Values() iter.Seq[T]

Iterate over treap values (leftmost to rightmost)

func (*Treap[T]) ValuesBackwards

func (t *Treap[T]) ValuesBackwards() iter.Seq[T]

Iterate over treap values in reverse order (rightmost to leftmost)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL