Skip to content

Commit adc50eb

Browse files
Update iterBinarySearch and add tests (#247)
Co-authored-by: Rak Laptudirm <[email protected]>
1 parent 26b0e2d commit adc50eb

File tree

4 files changed

+75
-33
lines changed

4 files changed

+75
-33
lines changed

search/binary.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
11
package search
22

3-
// Binary Binary Search
4-
func Binary(array []int, target int, lowIndex int, highIndex int) int {
3+
// Binary search for target within a sorted array by repeatedly dividing the array in half and comparing the midpoint with the target.
4+
// This function uses recursive call to itself.
5+
// If a target is found, the index of the target is returned. Else the function return -1 and ErrNotFound.
6+
func Binary(array []int, target int, lowIndex int, highIndex int) (int, error) {
57
if highIndex < lowIndex || len(array) == 0 {
6-
return -1
8+
return -1, ErrNotFound
79
}
8-
mid := (highIndex + lowIndex) / 2
10+
mid := int(lowIndex + (highIndex-lowIndex)/2)
911
if array[mid] > target {
1012
return Binary(array, target, lowIndex, mid-1)
1113
} else if array[mid] < target {
1214
return Binary(array, target, mid+1, highIndex)
1315
} else {
14-
return mid
16+
return mid, nil
1517
}
1618
}
1719

18-
// BinaryIterative Iterative Binary Search
19-
func BinaryIterative(array []int, target int, lowIndex int, highIndex int) int {
20+
// BinaryIterative search for target within a sorted array by repeatedly dividing the array in half and comparing the midpoint with the target.
21+
// Unlike Binary, this function uses iterative method and not recursive.
22+
// If a target is found, the index of the target is returned. Else the function return -1 and ErrNotFound.
23+
func BinaryIterative(array []int, target int, lowIndex int, highIndex int) (int, error) {
2024
startIndex := lowIndex
2125
endIndex := highIndex
2226
var mid int
23-
size := len(array)
24-
if size == 0 || highIndex > size || lowIndex < 0 {
25-
return -1
26-
}
2727
for startIndex <= endIndex {
28-
mid = (endIndex + startIndex) / 2
28+
mid = int(startIndex + (endIndex-startIndex)/2)
2929
if array[mid] > target {
3030
endIndex = mid - 1
3131
} else if array[mid] < target {
3232
startIndex = mid + 1
3333
} else {
34-
return mid
34+
return mid, nil
3535
}
3636
}
37-
return -1
37+
return -1, ErrNotFound
3838
}

search/binary_test.go

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,51 @@
11
package search
22

3-
import (
4-
"testing"
5-
)
3+
import "testing"
64

75
func TestBinarySearch(t *testing.T) {
86
for _, test := range searchTests {
9-
actual := Binary(test.data, test.key, 0, len(test.data)-1)
10-
if actual != test.expected {
11-
t.Errorf("test %s failed", test.name)
7+
actualValue, actualError := Binary(test.data, test.key, 0, len(test.data)-1)
8+
if actualValue != test.expected {
9+
t.Errorf("test '%s' failed: input array '%v' with key '%d', expected '%d', get '%d'", test.name, test.data, test.key, test.expected, actualValue)
10+
}
11+
if actualError != test.expectedError {
12+
t.Errorf("test '%s' failed: input array '%v' with key '%d', expected error '%s', get error '%s'", test.name, test.data, test.key, test.expectedError, actualError)
1213
}
1314
}
1415
}
1516

1617
func TestIterBinarySearch(t *testing.T) {
1718
for _, test := range searchTests {
18-
actual := BinaryIterative(test.data, test.key, 0, len(test.data)-1)
19-
if actual != test.expected {
20-
t.Errorf("test %s failed", test.name)
19+
actualValue, actualError := BinaryIterative(test.data, test.key, 0, len(test.data)-1)
20+
if actualValue != test.expected {
21+
t.Errorf("test '%s' failed: input array '%v' with key '%d', expected '%d', get '%d'", test.name, test.data, test.key, test.expected, actualValue)
22+
}
23+
if actualError != test.expectedError {
24+
t.Errorf("test '%s' failed: input array '%v' with key '%d', expected error '%s', get error '%s'", test.name, test.data, test.key, test.expectedError, actualError)
2125
}
2226
}
2327
}
28+
29+
func generateBenchmarkTestCase() []int {
30+
var testCase []int
31+
for i := 0; i < 100; i++ {
32+
testCase = append(testCase, i)
33+
}
34+
return testCase
35+
}
36+
37+
func BenchmarkBinarySearch(b *testing.B) {
38+
testCase := generateBenchmarkTestCase()
39+
b.ResetTimer() // this is important because the generateBenchmarkTestCase() is expensive
40+
for i := 0; i < b.N; i++ {
41+
_, _ = Binary(testCase, 10, 0, len(testCase)-1)
42+
}
43+
}
44+
45+
func BenchmarkIterBinarySearch(b *testing.B) {
46+
testCase := generateBenchmarkTestCase()
47+
b.ResetTimer() // this is important because the generateBenchmarkTestCase() is expensive
48+
for i := 0; i < b.N; i++ {
49+
_, _ = BinaryIterative(testCase, 10, 0, len(testCase)-1)
50+
}
51+
}

search/errors.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package search
2+
3+
import "errors"
4+
5+
// ErrNotFound is returned by search functions when target is not found
6+
var ErrNotFound = errors.New("target not found in array")

search/testcases.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
package search
22

33
type searchTest struct {
4-
data []int
5-
key int
6-
expected int
7-
name string
4+
data []int
5+
key int
6+
expected int
7+
expectedError error
8+
name string
89
}
910

1011
// Note that these are immutable therefore they are shared among all the search tests.
1112
// If your algorithm is mutating these then it is advisable to create separate test cases.
1213
var searchTests = []searchTest{
1314
//Sanity
14-
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 1, 0, "Sanity"},
15-
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 5, 4, "Sanity"},
16-
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 10, 9, "Sanity"},
15+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 10, 9, nil, "Sanity"},
16+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 9, 8, nil, "Sanity"},
17+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 8, 7, nil, "Sanity"},
18+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 7, 6, nil, "Sanity"},
19+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 6, 5, nil, "Sanity"},
20+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 5, 4, nil, "Sanity"},
21+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 4, 3, nil, "Sanity"},
22+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 3, 2, nil, "Sanity"},
23+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 2, 1, nil, "Sanity"},
24+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 1, 0, nil, "Sanity"},
1725
//Absent
18-
{[]int{1, 4, 5, 6, 7, 10}, -25, -1, "Absent"},
19-
{[]int{1, 4, 5, 6, 7, 10}, 25, -1, "Absent"},
26+
{[]int{1, 4, 5, 6, 7, 10}, -25, -1, ErrNotFound, "Absent"},
27+
{[]int{1, 4, 5, 6, 7, 10}, 25, -1, ErrNotFound, "Absent"},
2028
//Empty slice
21-
{[]int{}, 2, -1, "Empty"},
29+
{[]int{}, 2, -1, ErrNotFound, "Empty"},
2230
}

0 commit comments

Comments
 (0)