如何确定范围列表是否涵盖给定范围?

时间:2019-05-22 00:48:10

标签: algorithm sorting go search

我想有效地确定范围列表是否涵盖给定范围。例如,范围[(0-3),(3-5),(4-8),(6-10)]的列表涵盖范围(0-10),而[(5-10),(0- 3)]不。该列表可以包含重叠,并且不一定要排序。

我尝试实现如下所示的Continuous函数,该函数检查字节范围片是否包含给定的startend传递范围。

type byteRange struct {
    start int64
    end   int64
}

type byteRanges []*byteRange

func (brs byteRanges) Len() int {
    return len(brs)
}

func (brs byteRanges) Swap(i, j int) {
    brs[i], brs[j] = brs[j], brs[i]
}

func (brs byteRanges) Less(i, j int) bool {
    return brs[i].start < brs[j].start
}

func (brs byteRanges) Continuous(start int64, end int64) bool {
    curPos := start
    sort.Sort(brs)

    for _, br := range brs {
        if br.start > curPos+1 {
            return false
        }

        if curPos < br.end {
            curPos = br.end
        }

        if curPos >= end {
            return true
        }
    }

    return false
}

该函数的行为正确,但是在处理大量范围时以及经常调用时,它的性能不是很好。有人可以推荐可以加快这种逻辑速度的算法/实现吗?

2 个答案:

答案 0 :(得分:2)

由于您将在同一组范围内重复调用Continuous,因此,最好创建一个Condense方法(或任何您想调用的方法),该方法将切分并返回一个范围已排序且所有重叠范围已合并的新切片。您只需为任意一组给定的范围调用Condense 一次Continuous然后可以要求仅在Condense的结果上调用它。 (要强制执行此要求,最好让Condense实际上返回一个自定义类型的struct,该自定义类型只是一个切片的包装,并仅在其上定义Continuous struct类型。为方便起见,您可以定义一个单独的Continuous方法,可以直接在切片上调用 ,该方法调用Condense并然后是Continuous。这种便利方法当然会再次变慢,但是对于只被检查一次的集合可能会很便利。)

Condense中的合并逻辑非常简单:

  • 如果切片为空,则将其返回(提前退出)。
  • 按其start对范围进行排序。
  • 创建一个名为result的新切片。
  • prevRange初始化为第一个范围。
  • 遍历范围。为每一个:
    • 如果当前范围从prevRange.end + 1开始,请将prevRange添加到result,然后将prevRange设置为当前范围。
    • 否则,如果当前范围在prevRange.end之后结束,请将prevRange.end设置为当前范围的end
  • prevRange添加到result

Continuous中的逻辑现在可以是:

  • 对范围进行二进制搜索,找到start小于或等于start的最后一个范围。
  • 如果此范围的end大于或等于end,则返回true;否则,返回false

答案 1 :(得分:0)

更简单的解决方案?

package main

import (
    "fmt"
    "sort"
)

type byteRange struct {
    start int64
    end   int64
}

type byteRanges []*byteRange

func (brs byteRanges) Continuous(start int64, end int64) bool {
    sort.Slice(brs, func(i, j int) bool {
        return brs[i].start < brs[j].start
    })

    var (
        longestReach int64
        in           bool
    )

    for _, br := range brs {
        if !in {
            // first br satrts laying over the target
            if br.start <= start && br.end >= start {
                longestReach = br.end
                in = true
            }
            continue
        }
        if in && longestReach < br.start-1 {
            break
        }
        if longestReach < br.end {
            longestReach = br.end
        }
    }

    return in && longestReach >= end
}

func main() {
    brs := byteRanges{{5, 9}, {6, 10}}
    fmt.Println(brs.Continuous(8, 10))
}