From 1686dfad7ecb5b171821e3669f9cd7a2d0ad59b4 Mon Sep 17 00:00:00 2001 From: rdleal Date: Wed, 28 Feb 2024 21:47:02 -0300 Subject: [PATCH] feat: add MaxEnd to the interval search trees --- interval/search.go | 17 +++++++++ interval/search_test.go | 76 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/interval/search.go b/interval/search.go index 2a7fb3b..59368f2 100644 --- a/interval/search.go +++ b/interval/search.go @@ -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. diff --git a/interval/search_test.go b/interval/search_test.go index 4e73776..c737613 100644 --- a/interval/search_test.go +++ b/interval/search_test.go @@ -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)