Skip to content

Commit

Permalink
feat: add MaxEnd to the interval search trees
Browse files Browse the repository at this point in the history
  • Loading branch information
rdleal committed Feb 29, 2024
1 parent ff81910 commit 1686dfa
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 0 deletions.
17 changes: 17 additions & 0 deletions interval/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,23 @@ func (st *SearchTree[V, T]) Max() (V, bool) {
return val, true
}

// MaxEnd returns the values in the tree that have the largest ending interval.
// It returns false as the second return value if the tree is empty; otherwise, true.
func (st *SearchTree[V, T]) MaxEnd() ([]V, bool) {
st.mu.Lock()
defer st.mu.Unlock()

var vals []V
if st.root == nil {
return vals, false
}

maxEnd(st.root, st.root.maxEnd, st.cmp, func(n *node[V, T]) {
vals = append(vals, n.interval.val)
})
return vals, true
}

// Ceil returns a value which interval key is the smallest interval key greater than the given start and end interval.
// It returns true as the second return value if there's a ceiling interval key for the given start and end interval
// in the tree; otherwise, false.
Expand Down
76 changes: 76 additions & 0 deletions interval/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,82 @@ func TestSearchTree_Max_EmptyTree(t *testing.T) {
}
}

func TestSearchTree_MaxEnd(t *testing.T) {
type insert struct {
start int
end int
val string
}
tests := map[string]struct {
inserts []insert
expectedMaxEndStrings []string
}{
"single interval": {
inserts: []insert{
{start: 1, end: 10, val: "node1"},
},
expectedMaxEndStrings: []string{"node1"},
},
"multiple intervals": {
inserts: []insert{
{start: 1, end: 10, val: "node1"},
{start: 5, end: 15, val: "node2"},
{start: 10, end: 20, val: "node3"},
{start: 15, end: 25, val: "node4"},
{start: 20, end: 30, val: "node5"},
},
expectedMaxEndStrings: []string{"node5"},
},
"multiple intervals with same end": {
inserts: []insert{
{start: 1, end: 10, val: "node1"},
{start: 5, end: 15, val: "node2"},
{start: 10, end: 20, val: "node3"},
{start: 15, end: 25, val: "node4"},
{start: 20, end: 30, val: "node5"},
{start: 25, end: 30, val: "node6"},
},
expectedMaxEndStrings: []string{"node6", "node5"},
},
"multiple intervals with same end and same start": {
inserts: []insert{
{start: 20, end: 30, val: "node5"},
{start: 25, end: 30, val: "node6"},
{start: 15, end: 30, val: "node7"},
},
expectedMaxEndStrings: []string{"node5", "node7", "node6"},
},
"interval spanning entire range": {
inserts: []insert{
{start: 1, end: 5, val: "node1"},
{start: 5, end: 10, val: "node2"},
{start: 10, end: 20, val: "node3"},
{start: 0, end: 30, val: "node4"},
},
expectedMaxEndStrings: []string{"node4"},
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
st := NewSearchTree[string](func(x, y int) int { return x - y })

for _, insert := range test.inserts {
st.Insert(insert.start, insert.end, insert.val)
}

got, ok := st.MaxEnd()
if !ok {
t.Errorf("st.MaxEnd(): got no max end value")
}

if !reflect.DeepEqual(got, test.expectedMaxEndStrings) {
t.Errorf("st.MaxEnd(): got unexpected value %v; want %v", got, test.expectedMaxEndStrings)
}
})
}
}

func TestSearchTree_Ceil(t *testing.T) {
st := NewSearchTree[string](func(x, y int) int { return x - y })
defer mustBeValidTree(t, st.root)
Expand Down

0 comments on commit 1686dfa

Please sign in to comment.