CG_go_012_BinarySearch



In [1]:
import (
    "fmt"
    "math/rand"
    "sort"
)

In [2]:
// Return leftmost offset where val can be inserted in sl while
// maintaining sorted orter.  Assumes sl is sorted.
func bisectLeftInt(sl []int, val int) int {
    return sort.Search(len(sl), func(i int) bool {return sl[i] >= val})
}

In [3]:
// Same but for rightmost offset
func bisectRightInt(sl []int, val int) int {
    return sort.Search(len(sl), func(i int) bool {return sl[i] > val})
}

In [4]:
// Same as bisectLeftInt but where val and sl contain strings
func bisectLeftStr(sl []string, val string) int {
    return sort.Search(len(sl), func(i int) bool {return sl[i] >= val})
}

In [5]:
// Same but for rightmost offset
func bisectRightStr(sl []string, val string) int {
    return sort.Search(len(sl), func(i int) bool {return sl[i] > val})
}

In [6]:
a := []int{1, 2, 3, 3, 3, 4, 5}
bisectLeftInt(a, 3)


2

In [7]:
bisectRightInt(a, 3)


5

In [8]:
a = []int{2, 4, 6, 8, 10}
bisectLeftInt(a, 5)


2

In [9]:
bisectRightInt(a, 5)


2

In [10]:
// Make a list of pseudo-random integers in [1, 99]
rand.Seed(77)
ls := make([]int, 30)
for i := range ls {
    ls[i] = rand.Intn(100) + 1
}
ls


[61 71 6 70 39 55 13 19 33 71 1 72 15 34 62 72 77 52 23 95 29 9 86 40 28 50 31 59 25 62]

In [11]:
sort.Ints(ls)
ls


[1 6 9 13 15 19 23 25 28 29 31 33 34 39 40 50 52 55 59 61 62 62 70 71 71 72 72 77 86 95]

In [12]:
// The number 30 is not in the sorted list
// If it were, where would it go?
val := 30
insPt := bisectLeftInt(ls, val)
insPt


10

In [13]:
// Insert it
ls = append(ls, 0)
copy(ls[insPt+1:], ls[insPt:])
ls[insPt] = val
ls


[1 6 9 13 15 19 23 25 28 29 30 31 33 34 39 40 50 52 55 59 61 62 62 70 71 71 72 72 77 86 95]

In [14]:
// 62 appears multiple times
st := bisectLeftInt(ls, 62)
en := bisectRightInt(ls, 62)
st


21

In [15]:
en


23

In [16]:
ls[st:en]


[62 62]

In [17]:
// Of course, there also exists a total ordering over strings
// (lexicographical ordering), so we can do all the same things
// with strings
strls := []string{"a", "awkward", "awl", "awls", "axe", "axes", "bee"}
st = bisectLeftStr(strls, "awl")
en = bisectRightStr(strls, "awl")
st


2

In [18]:
en


3

In [19]:
// Say we want to get the range of elements that have some prefix,
// e.g. 'aw' and say that 'z' is the lexicographically greatest
// character in the alphabet.
st, en = bisectLeftStr(strls, "aw"), bisectLeftStr(strls, "ax")
st


1

In [20]:
en


4

In [21]:
strls[st:en]


[awkward awl awls]

In [22]:
// If we can do this for any sorted list of strings, we can do it for
// a sorted list of suffixes
t := "abaaba$"
suffixes := []string{t, t[1:], t[2:], t[3:], t[4:], t[5:], t[6:]}
sort.Strings(suffixes)
suffixes


[$ a$ aaba$ aba$ abaaba$ ba$ baaba$]

In [23]:
// Search for suffixes starting with aba
st = bisectLeftStr(suffixes, "aba")
en = bisectLeftStr(suffixes, "abb")
st


3

In [24]:
en


5