Merge pull request #7581 from heyitsanthony/ivt-sorted-visit

adt: Visit() interval trees in sorted order
This commit is contained in:
Anthony Romano 2017-03-23 14:11:08 -07:00 committed by GitHub
commit 3f8eab8439
2 changed files with 108 additions and 8 deletions

View File

@ -134,25 +134,29 @@ func (x *intervalNode) updateMax() {
type nodeVisitor func(n *intervalNode) bool
// visit will call a node visitor on each node that overlaps the given interval
func (x *intervalNode) visit(iv *Interval, nv nodeVisitor) {
func (x *intervalNode) visit(iv *Interval, nv nodeVisitor) bool {
if x == nil {
return
return true
}
v := iv.Compare(&x.iv.Ivl)
switch {
case v < 0:
x.left.visit(iv, nv)
if !x.left.visit(iv, nv) {
return false
}
case v > 0:
maxiv := Interval{x.iv.Ivl.Begin, x.max}
if maxiv.Compare(iv) == 0 {
x.left.visit(iv, nv)
x.right.visit(iv, nv)
if !x.left.visit(iv, nv) || !x.right.visit(iv, nv) {
return false
}
}
default:
nv(x)
x.left.visit(iv, nv)
x.right.visit(iv, nv)
if !x.left.visit(iv, nv) || !nv(x) || !x.right.visit(iv, nv) {
return false
}
}
return true
}
type IntervalValue struct {
@ -406,6 +410,7 @@ func (ivt *IntervalTree) MaxHeight() int {
type IntervalVisitor func(n *IntervalValue) bool
// Visit calls a visitor function on every tree node intersecting the given interval.
// It will visit each interval [x, y) in ascending order sorted on x.
func (ivt *IntervalTree) Visit(ivl Interval, ivv IntervalVisitor) {
ivt.root.visit(&ivl, func(n *intervalNode) bool { return ivv(&n.iv) })
}

View File

@ -136,3 +136,98 @@ func TestIntervalTreeRandom(t *testing.T) {
t.Errorf("got ivt.Len() = %v, expected 0", ivt.Len())
}
}
// TestIntervalTreeSortedVisit tests that intervals are visited in sorted order.
func TestIntervalTreeSortedVisit(t *testing.T) {
tests := []struct {
ivls []Interval
visitRange Interval
}{
{
ivls: []Interval{NewInt64Interval(1, 10), NewInt64Interval(2, 5), NewInt64Interval(3, 6)},
visitRange: NewInt64Interval(0, 100),
},
{
ivls: []Interval{NewInt64Interval(1, 10), NewInt64Interval(10, 12), NewInt64Interval(3, 6)},
visitRange: NewInt64Interval(0, 100),
},
{
ivls: []Interval{NewInt64Interval(2, 3), NewInt64Interval(3, 4), NewInt64Interval(6, 7), NewInt64Interval(5, 6)},
visitRange: NewInt64Interval(0, 100),
},
{
ivls: []Interval{
NewInt64Interval(2, 3),
NewInt64Interval(2, 4),
NewInt64Interval(3, 7),
NewInt64Interval(2, 5),
NewInt64Interval(3, 8),
NewInt64Interval(3, 5),
},
visitRange: NewInt64Interval(0, 100),
},
}
for i, tt := range tests {
ivt := &IntervalTree{}
for _, ivl := range tt.ivls {
ivt.Insert(ivl, struct{}{})
}
last := tt.ivls[0].Begin
count := 0
chk := func(iv *IntervalValue) bool {
if last.Compare(iv.Ivl.Begin) > 0 {
t.Errorf("#%d: expected less than %d, got interval %+v", i, last, iv.Ivl)
}
last = iv.Ivl.Begin
count++
return true
}
ivt.Visit(tt.visitRange, chk)
if count != len(tt.ivls) {
t.Errorf("#%d: did not cover all intervals. expected %d, got %d", i, len(tt.ivls), count)
}
}
}
// TestIntervalTreeVisitExit tests that visiting can be stopped.
func TestIntervalTreeVisitExit(t *testing.T) {
ivls := []Interval{NewInt64Interval(1, 10), NewInt64Interval(2, 5), NewInt64Interval(3, 6), NewInt64Interval(4, 8)}
ivlRange := NewInt64Interval(0, 100)
tests := []struct {
f IntervalVisitor
wcount int
}{
{
f: func(n *IntervalValue) bool { return false },
wcount: 1,
},
{
f: func(n *IntervalValue) bool { return n.Ivl.Begin.Compare(ivls[0].Begin) <= 0 },
wcount: 2,
},
{
f: func(n *IntervalValue) bool { return n.Ivl.Begin.Compare(ivls[2].Begin) < 0 },
wcount: 3,
},
{
f: func(n *IntervalValue) bool { return true },
wcount: 4,
},
}
for i, tt := range tests {
ivt := &IntervalTree{}
for _, ivl := range ivls {
ivt.Insert(ivl, struct{}{})
}
count := 0
ivt.Visit(ivlRange, func(n *IntervalValue) bool {
count++
return tt.f(n)
})
if count != tt.wcount {
t.Errorf("#%d: expected count %d, got %d", i, tt.wcount, count)
}
}
}