You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// If we get here, then we've found the search key!
78
+
} else {
79
+
return midIndex
80
+
}
81
+
}
82
82
}
83
83
```
84
84
@@ -94,84 +94,84 @@ Note that the `numbers` array is sorted. The binary search algorithm does not wo
94
94
95
95
I said that binary search works by splitting the array in half, but we don't actually create two new arrays. Instead, we keep track of these splits using a Swift `Range` object. Initially, this range covers the entire array, `0 ..< numbers.count`. As we split the array, the range becomes smaller and smaller.
96
96
97
-
> **Note:** One thing to be aware of is that `range.endIndex` always points one beyond the last element. In the example, the range is `0..<19` because there are 19 numbers in the array, and so `range.startIndex = 0` and `range.endIndex = 19`. But in our array the last element is at index 18, not 19, since we start counting from 0. Just keep this in mind when working with ranges: the `endIndex` is always one more than the index of the last element.
97
+
> **Note:** One thing to be aware of is that `range.upperBound` always points one beyond the last element. In the example, the range is `0..<19` because there are 19 numbers in the array, and so `range.lowerBound = 0` and `range.upperBound = 19`. But in our array the last element is at index 18, not 19, since we start counting from 0. Just keep this in mind when working with ranges: the `upperBound` is always one more than the index of the last element.
98
98
99
99
## Stepping through the example
100
100
101
101
It might be useful to look at how the algorithm works in detail.
102
102
103
103
The array from the above example consists of 19 numbers and looks like this when sorted:
We're trying to determine if the number `43` is in this array.
108
108
109
109
To split the array in half, we need to know the index of the object in the middle. That's determined by this line:
110
110
111
111
```swift
112
-
let midIndex = range.startIndex+ (range.endIndex- range.startIndex) /2
112
+
let midIndex = range.lowerBound+ (range.upperBound- range.lowerBound) /2
113
113
```
114
114
115
-
Initially, the range has `startIndex = 0` and `endIndex = 19`. Filling in these values, we find that `midIndex` is `0 + (19 - 0)/2 = 19/2 = 9`. It's actually `9.5` but because we're using integers, the answer is rounded down.
115
+
Initially, the range has `lowerBound = 0` and `upperBound = 19`. Filling in these values, we find that `midIndex` is `0 + (19 - 0)/2 = 19/2 = 9`. It's actually `9.5` but because we're using integers, the answer is rounded down.
116
116
117
117
In the next figure, the `*` shows the middle item. As you can see, the number of items on each side is the same, so we're split right down the middle.
Now binary search will determine which half to use. The relevant section from the code is:
123
123
124
124
```swift
125
-
if a[midIndex] > key {
126
-
// use left half
127
-
} elseif a[midIndex] < key {
128
-
// use right half
129
-
} else {
130
-
return midIndex
131
-
}
125
+
if a[midIndex] > key {
126
+
// use left half
127
+
} elseif a[midIndex] < key {
128
+
// use right half
129
+
} else {
130
+
return midIndex
131
+
}
132
132
```
133
133
134
134
In this case, `a[midIndex] = 29`. That's less than the search key, so we can safely conclude that the search key will never be in the left half of the array. After all, the left half only contains numbers smaller than `29`. Hence, the search key must be in the right half somewhere (or not in the array at all).
135
135
136
-
Now we can simply repeat the binary search, but on the array interval from `midIndex + 1` to `range.endIndex`:
136
+
Now we can simply repeat the binary search, but on the array interval from `midIndex + 1` to `range.upperBound`:
Since we no longer need to concern ourselves with the left half of the array, I've marked that with `x`'s. From now on we'll only look at the right half, which starts at array index 10.
141
141
142
142
We calculate the index of the new middle element: `midIndex = 10 + (19 - 10)/2 = 14`, and split the array down the middle again.
As you can see, `a[14]` is indeed the middle element of the array's right half.
148
148
149
149
Is the search key greater or smaller than `a[14]`? It's smaller because `43 < 47`. This time we're taking the left half and ignore the larger numbers on the right:
The search key is greater than `37`, so continue with the right side:
159
159
160
-
[ x, x, x, x, x, x, x, x, x, x | x, x | 41, 43 | x, x, x, x, x ]
161
-
*
160
+
[ x, x, x, x, x, x, x, x, x, x | x, x | 41, 43 | x, x, x, x, x ]
161
+
*
162
162
163
163
Again, the search key is greater, so split once more and take the right side:
164
164
165
-
[ x, x, x, x, x, x, x, x, x, x | x, x | x | 43 | x, x, x, x, x ]
166
-
*
165
+
[ x, x, x, x, x, x, x, x, x, x | x, x | x | 43 | x, x, x, x, x ]
166
+
*
167
167
168
168
And now we're done. The search key equals the array element we're looking at, so we've finally found what we were searching for: number `43` is at array index `13`. w00t!
169
169
170
170
It may have seemed like a lot of work, but in reality it only took four steps to find the search key in the array, which sounds about right because `log_2(19) = 4.23`. With a linear search, it would have taken 14 steps.
171
171
172
-
What would happen if we were to search for `42` instead of `43`? In that case, we can't split up the array any further. The `range.endIndex` becomes smaller than `range.startIndex`. That tells the algorithm the search key is not in the array and it returns `nil`.
172
+
What would happen if we were to search for `42` instead of `43`? In that case, we can't split up the array any further. The `range.upperBound` becomes smaller than `range.lowerBound`. That tells the algorithm the search key is not in the array and it returns `nil`.
173
173
174
-
> **Note:** Many implementations of binary search calculate `midIndex = (startIndex + endIndex) / 2`. This contains a subtle bug that only appears with very large arrays, because `startIndex + endIndex` may overflow the maximum number an integer can hold. This situation is unlikely to happen on a 64-bit CPU, but it definitely can on 32-bit machines.
174
+
> **Note:** Many implementations of binary search calculate `midIndex = (lowerBound + upperBound) / 2`. This contains a subtle bug that only appears with very large arrays, because `lowerBound + upperBound` may overflow the maximum number an integer can hold. This situation is unlikely to happen on a 64-bit CPU, but it definitely can on 32-bit machines.
175
175
176
176
## Iterative vs recursive
177
177
@@ -181,18 +181,19 @@ Here is an iterative implementation of binary search in Swift:
0 commit comments