Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MaxEnd: find the interval values that contain the max end of the tree #8

Merged
merged 4 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions interval/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,3 +426,37 @@ func (st *MultiValueSearchTree[V, T]) Select(k int) ([]V, bool) {

return interval.vals, 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 *MultiValueSearchTree[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.vals...)
})
return vals, true
}

func maxEnd[V, T any](n *node[V, T], searchEnd T, cmp CmpFunc[T], visit func(*node[V, T])) {

// If this node's interval lines up with maxEnd, visit it.
if cmp.eq(n.interval.end, searchEnd) {
visit(n)
}

// Search left if the left subtree contains a max ending interval that is equal to the root's max ending interval.
if n.left != nil && cmp.eq(n.left.maxEnd, searchEnd) {
maxEnd(n.left, searchEnd, cmp, visit)
}

// Search right if the right subtree contains a max ending interval that is equal to the root's max ending interval.
if n.right != nil && cmp.eq(n.right.maxEnd, searchEnd) {
maxEnd(n.right, searchEnd, cmp, visit)
}
}
77 changes: 77 additions & 0 deletions interval/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -962,3 +962,80 @@ func TestMultiValueSearchTree_Select(t *testing.T) {
})
}
}

func TestMultiValueSearchTree_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: 25, end: 30, val: "node7"},
},
expectedMaxEndStrings: []string{"node6", "node7", "node5"},
},
"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 := NewMultiValueSearchTree[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)
}
})
}

}
Loading