# Swift 算法实战之路：二分搜索

• 基本概念
• 实战演练
• iOS中搜索与排序的配合使用

# 基本概念

Swift 实现二分搜索

``````// 假设nums是一个升序数组
func binarySearch(_ nums: [Int], _ target: Int) -> Bool {
var left = 0, mid = 0, right = nums.count - 1

while left <= right {
mid = (right - left) / 2 + left

if nums[mid] == target {
return true
} else if nums[mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}

return false
}
``````

``````func binarySearch(nums: [Int], target: Int) -> Bool {
return binarySearch(nums: nums, target: target, left: 0, right: nums.count - 1)
}

func binarySearch(nums: [Int], target: Int, left: Int, right: Int) -> Bool {
guard left <= right else {
return false
}

let mid = (right - left) / 2 + left

if nums[mid] == target {
return true
} else if nums[mid] < target {
return binarySearch(nums: nums, target: target, left: mid + 1, right: right)
} else {
return binarySearch(nums: nums, target: target, left: left, right: mid - 1)
}
}
``````

# 实战演练

### 第一题：版本崩溃

``````func findFirstBadVersion(version: n) -> Int {
// 处理特殊情况
guard n >= 1 else {
return -1
}

var left = 1, right = n, mid = 0

while left < right {
mid = (right - left) / 2 + left
right = mid
} else {
left = mid + 1
}
}

return left    // return right 同样正确
}
``````

1. 当发现中间版本(mid)是崩溃版本的时候，只能说明第一个崩溃的版本小于等于中间版本。所以只能写成 right = mid
2. 当检测到剩下一个版本的时候，我们已经无需在检测直接返回即可，因为它肯定是崩溃的版本。所以while循环不用写成left <= right

### 第二题：搜索旋转有序数组

``````func search(nums: [Int], target: Int) -> Int {
var (left, mid, right) = (0, 0, nums.count - 1)

while left <= right {
mid = (right - left) / 2 + left

if nums[mid] == target {
return mid
}

if nums[mid] >= nums[left] {
if nums[mid] > target && target >= nums[left] {
right = mid - 1
} else {
left = mid + 1
}
} else {
if nums[mid] < target && target <= nums[right] {
left = mid + 1
} else {
right = mid - 1
}
}
}

return -1
}
``````

# iOS中搜索与排序的配合使用

``````extension Array {
func indexForInsertingObject(object: AnyObject, compare: ((a: AnyObject, b: AnyObject) -> Int)) -> Int {
var left = 0
var right = self.count
var mid = 0

while left < right {
mid = (right - left) / 2 + left

if compare(a: self[mid] as! AnyObject, b: object) < 0 {
left = mid + 1
} else {
right = mid
}
}

return left
}
}
``````

``````let insertIdx = news.indexForInsertingObject(object: singleNews) { (a, b) -> Int in
let newsA = a as! News
let newsB = b as! News
return newsA.compareDate(newsB)
}

news.insert(singleNews, at: insertIdx)
``````