PQueue is a high-performance Go library that provides intelligent priority queue operations and adaptive sorting algorithms. It automatically selects the optimal sorting algorithm based on data characteristics, ensuring maximum performance across different scenarios.
- 🧠 Intelligent Algorithm Selection: Automatically chooses the best sorting algorithm based on data type, size, and patterns
- 🚀 High Performance: Optimized implementations of multiple sorting algorithms
- 🔧 Generic Support: Works with any comparable type using Go generics
- 📊 Priority Queue Operations: Push, pop, peek operations with automatic ordering
- 🎯 Multiple Data Types: Built-in support for integers, floats, strings, and custom types
- 📈 Comprehensive Testing: Extensive test suite with benchmarks
package main
import (
"fmt"
"github.com/mew-sh/pqueue"
)
func main() {
// Example as requested
a := []int{6, 5, 4, 9, 2, 7, 1, 8}
p := pqueue.NewInts(a)
// Pop minimum element
min, _ := p.Pop()
fmt.Printf("Popped: %d\n", min) // Output: 1
// Sort remaining elements
p.Sort()
fmt.Printf("Sorted: %v\n", p.ToSlice()) // Output: [2 4 5 6 7 8 9]
}
PQueue provides comprehensive support for all Go data types through intelligent type inference and optimized algorithms:
- Integers:
int
,int8
,int16
,int32
,int64
,uint
,uint8
,uint16
,uint32
,uint64
- Floating Point:
float32
,float64
- Strings:
string
- Byte Slices:
[]byte
- Rune Slices:
[]rune
- Slices:
[]T
(slice of any type) - Arrays:
[N]T
(fixed-size arrays) - Structs: Custom struct types with comparison functions
- Pointers:
*T
(with nil-safe comparisons) - Maps:
map[K]V
(with custom comparison logic) - Interfaces: Interface types
- Channels:
chan T
- Functions:
func
types
// Basic types
intQueue := pqueue.NewInts([]int{3, 1, 4, 1, 5})
floatQueue := pqueue.NewFloats([]float64{3.14, 2.71, 1.41})
stringQueue := pqueue.NewStrings([]string{"zebra", "apple", "banana"})
// Complex types
byteQueue := pqueue.NewBytes([][]byte{[]byte("hello"), []byte("world")})
runeQueue := pqueue.NewRunes([][]rune{[]rune("世界"), []rune("hello")})
// Generic constructor for any type
customQueue := pqueue.New(data, func(a, b CustomType) bool {
return a.Field < b.Field
})
// For comparable types
comparableQueue := pqueue.NewComparable(data, lessFunc)
The library automatically selects the optimal algorithm based on data characteristics:
Scenario | Optimization Algorithm | Use Case |
---|---|---|
Very Small Arrays (≤16) | Insertion Sort | Minimal overhead for tiny datasets |
Nearly Sorted Data | Insertion Sort | Exploits existing order |
Small Integer Range | Counting Sort | Linear time for bounded integers |
Large Integer Data | Radix Sort | Non-comparative sorting |
Large General Data | Introsort | Hybrid approach with guaranteed O(n log n) |
Real-world Data | Timsort | Adaptive to real-world patterns |
Distributed/Parallel | Merge Sort | Stable and parallelizable |
// For integers
pq := pqueue.NewInts([]int{3, 1, 4, 1, 5})
// For floats
pq := pqueue.NewFloats([]float64{3.14, 2.71, 1.41})
// For strings
pq := pqueue.NewStrings([]string{"apple", "banana", "cherry"})
// For custom types with custom comparison
pq := pqueue.New(data, func(a, b MyType) bool { return a.Value < b.Value })
// Priority Queue Operations
size := pq.Size() // Get number of elements
isEmpty := pq.IsEmpty() // Check if empty
pq.Push(item) // Add element
item, err := pq.Pop() // Remove and return minimum
item, err := pq.Peek() // Get minimum without removing
// Sorting Operations
pq.Sort() // Auto-select best algorithm
pq.SortWithStrategy(pqueue.QuickStrategy) // Use specific algorithm
data := pq.ToSlice() // Get sorted copy
type SortStrategy int
const (
AutoStrategy // Automatic selection (default)
RadixStrategy // Radix sort (integers only)
CountingStrategy // Counting sort (small range integers)
InsertionStrategy // Insertion sort (small/nearly sorted data)
TimsortStrategy // Timsort (adaptive merge sort)
IntrosortStrategy // Introsort (quick + heap + insertion)
MergeStrategy // Merge sort (stable, parallelizable)
QuickStrategy // Quick sort (general purpose)
)
// Small data → Insertion Sort
smallData := []int{3, 1, 4, 1, 5}
pq := pqueue.NewInts(smallData)
pq.Sort() // Uses insertion sort automatically
// Nearly sorted → Insertion Sort
nearlySorted := []int{1, 2, 3, 5, 4, 6, 7}
pq2 := pqueue.NewInts(nearlySorted)
pq2.Sort() // Detects pattern, uses insertion sort
// Large dataset → Introsort
largeData := make([]int, 10000)
// ... populate with random data
pq3 := pqueue.NewInts(largeData)
pq3.Sort() // Uses introsort for guaranteed performance
data := []int{170, 45, 75, 90, 2, 802, 24, 66}
pq := pqueue.NewInts(data)
// Force specific algorithms
pq.SortWithStrategy(pqueue.RadixStrategy) // Good for integers
pq.SortWithStrategy(pqueue.MergeStrategy) // Stable sort
pq.SortWithStrategy(pqueue.CountingStrategy) // Small range integers
Performance comparison on 5000 random integers:
Auto Selection: 245.2µs
Quick Sort: 312.8µs
Merge Sort: 387.1µs
Introsort: 251.4µs
Timsort: 298.7µs
Auto selection chooses the optimal algorithm, often outperforming manual selection.
go mod init your-project
go get github.com/mew-sh/pqueue
type Person struct {
Name string
Age int
}
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
}
// Sort by age
pq := pqueue.New(people, func(a, b Person) bool {
return a.Age < b.Age
})
pq.Sort()
sorted := pq.ToSlice() // Sorted by age
// Sort by multiple criteria
pq := pqueue.New(people, func(a, b Person) bool {
if a.Age == b.Age {
return a.Name < b.Name
}
return a.Age < b.Age
})
Run the comprehensive test suite:
go test ./... # Run all tests
go test -bench=. # Run benchmarks
go test -v # Verbose output
go test -race # Race condition detection
See the example directory for comprehensive usage examples including:
- Basic priority queue operations
- Automatic algorithm selection
- Performance comparisons
- Different data types
- Custom comparison functions
Run the example:
go run example/main.go
// 1. Numeric Types
integers := []int{6, 5, 4, 9, 2, 7, 1, 8}
intQueue := pqueue.NewInts(integers)
intQueue.Sort()
fmt.Println(intQueue.ToSlice()) // [1 2 4 5 6 7 8 9]
floats := []float64{3.14, 2.71, 1.41, 1.73}
floatQueue := pqueue.NewFloats(floats)
floatQueue.Sort()
fmt.Println(floatQueue.ToSlice()) // [1.41 1.73 2.71 3.14]
// 2. String Types
strings := []string{"zebra", "apple", "banana"}
stringQueue := pqueue.NewStrings(strings)
stringQueue.Sort()
fmt.Println(stringQueue.ToSlice()) // [apple banana zebra]
// Unicode strings with runes
words := [][]rune{[]rune("世界"), []rune("hello"), []rune("αβγ")}
runeQueue := pqueue.NewRunes(words)
runeQueue.Sort()
// Byte slices
byteSlices := [][]byte{[]byte("zebra"), []byte("apple")}
byteQueue := pqueue.NewBytes(byteSlices)
byteQueue.Sort()
// 3. Custom Struct Types
type Person struct {
Name string
Age int
}
people := []Person{
{Name: "Alice", Age: 30},
{Name: "Bob", Age: 25},
{Name: "Charlie", Age: 35},
}
// Sort by age, then by name
personQueue := pqueue.New(people, func(a, b Person) bool {
if a.Age != b.Age {
return a.Age < b.Age
}
return a.Name < b.Name
})
personQueue.Sort()
// 4. Pointer Types
values := []int{5, 2, 8, 1, 9}
ptrs := make([]*int, len(values))
for i := range values {
ptrs[i] = &values[i]
}
ptrQueue := pqueue.New(ptrs, func(a, b *int) bool {
if a == nil || b == nil {
return a == nil && b != nil // nil values first
}
return *a < *b
})
ptrQueue.Sort()
// 5. Slice Types (2D arrays)
slicesOfInts := [][]int{
{3, 2, 1},
{1, 2, 3},
{2, 1, 3},
}
sliceQueue := pqueue.New(slicesOfInts, func(a, b []int) bool {
// Custom comparison: by sum, then lexicographically
sumA, sumB := sum(a), sum(b)
if sumA != sumB {
return sumA < sumB
}
for i := 0; i < len(a) && i < len(b); i++ {
if a[i] != b[i] {
return a[i] < b[i]
}
}
return len(a) < len(b)
})
sliceQueue.Sort()
// 6. Interface Types
type Comparable interface {
CompareTo(other interface{}) int
}
// For types implementing Comparable interface
comparableQueue := pqueue.NewWithComparable([]ComparableType{...})
- Best Case: O(n) for nearly sorted data
- Average/Worst: O(n²)
- Use: Small arrays (≤16 elements), nearly sorted data
- Average: O(n log n)
- Worst: O(n²)
- Use: General purpose, good cache performance
- All Cases: O(n log n)
- Use: Stable sorting, parallel processing
- Space: O(n)
- All Cases: O(n log n) guaranteed
- Use: Large datasets, unknown data patterns
- Features: Hybrid of quicksort, heapsort, and insertion sort
- Best: O(n) for real-world data
- Average/Worst: O(n log n)
- Use: Real-world data with existing patterns
- Complexity: O(d × n) where d is number of digits
- Use: Integers, strings with fixed-length keys
- Space: O(n + k)
- Complexity: O(n + k) where k is range
- Use: Small range integers
- Space: O(k)
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by the intelligent sorting strategies used in production systems
- Algorithm implementations based on proven computer science research
- Performance optimizations derived from real-world usage patterns