Swift:标准数组的二进制搜索?
Posted
技术标签:
【中文标题】Swift:标准数组的二进制搜索?【英文标题】:Swift: Binary search for standard array? 【发布时间】:2015-11-01 11:20:27 【问题描述】:我有一个排序数组,想对其进行二分搜索。
所以我想问一下 Swift 库中是否已经提供了一些东西,比如 sort 等?或者是否有可用的类型独立版本?
我当然可以自己写,但我想避免重新发明***。
【问题讨论】:
你需要排序功能还是二分查找功能? 不知道过滤器有多快。它适用于每个阵列。但我知道我的数组已排序。所以我可以使用二进制来提高速度。 当然,这就是我切换的原因。我的数组中有 > 1.700.000 个字符串。我又使用了这个搜索一万次。 如果您使用此页面上的任何实现,值得强调的是Binary Search is notoriously hard to get right。我强烈建议对您使用的任何代码进行测试。例如,Java 的Arrays.binarySearch()
在 SDK 6.0 版本之前被破坏。作为my own answer 的插件,它包括测试。
如果您想查看完整的算法库或查看特定的 BS,这里是 Swift Algorithms Club repo github.com/raywenderlich/swift-algorithm-club/blob/master/…
【参考方案1】:
这是在 swift 5 中创建二分搜索函数的方法,在此示例中,我假设您要查找的项目保证在列表中,但是如果您的项目不能保证在列表中,那么您可以先运行这段代码检查一下:
yourList.contains(yourItem) //will return true or false
这里是二分查找函数:
override func viewDidLoad()
super.viewDidLoad()
print(binarySearch(list: [1, 2, 4, 5, 6], num: 6)) //returns 4
func binarySearch(list: [Int], num: Int) -> Int //returns index of num
var firstIndex = 0
var lastIndex = list.count - 1
var middleIndex = (firstIndex + lastIndex) / 2
var middleValue = list[middleIndex]
while true //loop until we find the item we are looking for
middleIndex = (firstIndex + lastIndex) / 2 //getting the list's middle index
middleValue = list[middleIndex]
if middleValue > num
lastIndex = middleIndex - 1 //get the left side of the remaining list
else if middleValue < num
firstIndex = middleIndex + 1 //get the right side of the remaining list
else if middleValue == num
break //found the correct value so we can break out of the loop
return middleIndex
我制作了一个 youtube 视频来解释这个here
【讨论】:
【参考方案2】:为了完整起见,这里是一个完全基于模式匹配的实现:
extension Collection where Element: Comparable
func binarySearch(for element: Element) -> Index?
switch index(startIndex, offsetBy: distance(from: startIndex, to: endIndex) / 2)
case let i where i >= endIndex: return nil
case let i where self[i] == element: return i
case let i where self[i] > element: return self[..<i].binarySearch(for: element)
case let i: return self[index(after: i)..<endIndex].binarySearch(for: element)
上面的代码应该适用于任何类型的集合,切片或不切片,零偏移或非零偏移。
【讨论】:
【参考方案3】:另一种实现方式:如果您希望结构或类可搜索而不使它们成为Comparable
,则改为使用BinarySearchable
:
public protocol BinarySearchable
associatedtype C: Comparable
var searchable: C get
public extension Array where Element: BinarySearchable
func binarySearch(_ prefix: Element.C) -> Index
var low = 0
var high = count
while low != high
let mid = (low + high) / 2
if self[mid].searchable < prefix
low = mid + 1
else
high = mid
return low
应按name
排序和搜索的结构的示例用法:
struct Country: BinraySearchable
var code: String
var name: String
var searchable: String name
// Suppose you have a list of countries sorted by `name`, you want to find
// the index of the first country whose name starts with "United", others
// will follow:
let index = listOfCountries.binarySearch("United")
【讨论】:
BinarySearchable
类型没有意义,你可以直接使用Comparable
代替。
@ErikAigner 如果搜索键不是对象本身,则不是。假设您有一个带有id
的对象,并且您想按id
进行排序和搜索,但该数组存储了整个对象。您单独定义“可搜索”属性,因此需要单独的协议 BinarySearchable。
这样你可以让一个对象只能被一个属性搜索,因为你只能扩展它一次。如果您直接使用Comparable
,您可以将存储的对象包装在实现您想要的比较行为的struct
中。
@ErikAigner 我知道,但仅通过一个属性使对象可比较可能是不可取的,甚至是危险的——想想意外的比较。 Binray 搜索是一个非常具体的功能,我更喜欢将这种类型的比较限制在它上面。【参考方案4】:
这是使用二分搜索的通用方法:
func binarySearch<T:Comparable>(_ inputArr:Array<T>, _ searchItem: T) -> Int?
var lowerIndex = 0
var upperIndex = inputArr.count - 1
while (true)
let currentIndex = (lowerIndex + upperIndex)/2
if(inputArr[currentIndex] == searchItem)
return currentIndex
else if (lowerIndex > upperIndex)
return nil
else
if (inputArr[currentIndex] > searchItem)
upperIndex = currentIndex - 1
else
lowerIndex = currentIndex + 1
var myArray = [1,2,3,4,5,6,7,9,10]
if let searchIndex = binarySearch(myArray, 5)
print("Element found on index: \(searchIndex)")
【讨论】:
在没有匹配的情况下不返回-1
会大大改善这一点。更类似于 Swift 的方法是返回一个可选项。另一种好的方法是在找不到元素时返回endIndex
。
@Benjohn 完全同意可选的,答案是 2 岁,是时候编辑它了 :)
也可以应用于字符串?
@RajuyourPepe 用于符合Comparable
协议的类
您应该在开头添加guard !inputArr.isEmpty else return nil
以解决空数组的情况。另外,我认为 else if 条件应该是lowerIndex >= upperIndex
【参考方案5】:
详情
Swift 5.2、Xcode 11.4 (11E146)解决方案
import Foundation
extension RandomAccessCollection where Element: Comparable
private func binarySearchIteration(forIndexOf value: Element, in range: Range<Index>? = nil,
valueDetected: ((Index, _ in: Range<Index>) -> Index?)) -> Index?
let range = range ?? startIndex..<endIndex
guard range.lowerBound < range.upperBound else return nil
let size = distance(from: range.lowerBound, to: range.upperBound)
let middle = index(range.lowerBound, offsetBy: size / 2)
switch self[middle]
case value: return valueDetected(middle, range) ?? middle
case ..<value: return binarySearch(forIndexOf: value, in: index(after: middle)..<range.upperBound)
default: return binarySearch(forIndexOf: value, in: range.lowerBound..<middle)
func binarySearch(forIndexOf value: Element, in range: Range<Index>? = nil) -> Index?
binarySearchIteration(forIndexOf: value, in: range) currentIndex, _ in currentIndex
func binarySearch(forFirstIndexOf value: Element, in range: Range<Index>? = nil) -> Index?
binarySearchIteration(forIndexOf: value, in: range) currentIndex, range in
binarySearch(forFirstIndexOf: value, in: range.lowerBound..<currentIndex)
func binarySearch(forLastIndexOf value: Element, in range: Range<Index>? = nil) -> Index?
binarySearchIteration(forIndexOf: value, in: range) currentIndex, range in
binarySearch(forFirstIndexOf: value, in: index(after: currentIndex)..<range.upperBound)
func binarySearch(forIndicesRangeOf value: Element, in range: Range<Index>? = nil) -> Range<Index>?
let range = range ?? startIndex..<endIndex
guard range.lowerBound < range.upperBound else return nil
guard let currentIndex = binarySearchIteration(forIndexOf: value, in: range, valueDetected: index, _ in index
) else return nil
let firstIndex = binarySearch(forFirstIndexOf: value, in: range.lowerBound ..< index(after: currentIndex)) ?? currentIndex
let lastIndex = binarySearch(forFirstIndexOf: value, in: index(after: currentIndex) ..< range.upperBound) ?? currentIndex
return firstIndex..<index(after: lastIndex)
用法
//let array = ["one", "two", "three", "three", "three", "three", "three", "four", "five", "five"]
//let value = "three"
let array = [1, 2, 3, 3, 3, 3, 3, 4, 5, 5]
let value = 3
print(array.binarySearch(forFirstIndexOf: value))
print(array.binarySearch(forLastIndexOf: value))
print(array.binarySearch(forIndicesRangeOf: value))
测试
protocol _BinarySearchTestable: class where Collection: RandomAccessCollection, Collection.Element: Comparable
associatedtype Collection
var array: Collection! get set
var elementToSearch: Collection.Element! get set
func testFindFirstIndexOfValueInCollection()
func testFindLastIndexOfValueInCollection()
func testFindIndicesRangeOfValueInCollection()
extension _BinarySearchTestable where Self: XCTest
typealias Element = Collection.Element
typealias Index = Collection.Index
func _testFindFirstIndexOfValueInCollection()
_testfindFirstIndex(comparableArray: array, testableArray: array)
func _testFindLastIndexOfValueInCollection()
let index1 = array.lastIndex(of: elementToSearch)
let index2 = array.binarySearch(forLastIndexOf: elementToSearch)
_testElementsAreEqual(indexInComparableArray: index1, comparableArray: array,
indexInTestableArray: index2, testableArray: array)
func _testFindIndicesRangeOfValueInCollection()
var range1: Range<Index>?
if let firstIndex = array.firstIndex(of: elementToSearch),
let lastIndex = array.lastIndex(of: elementToSearch)
range1 = firstIndex ..< array.index(after: lastIndex)
let range2 = array.binarySearch(forIndicesRangeOf: elementToSearch)
XCTAssertEqual(range1, range2)
private func _testElementsAreEqual(indexInComparableArray: Index?, comparableArray: Collection,
indexInTestableArray: Index?, testableArray: Collection)
XCTAssertEqual(indexInComparableArray, indexInTestableArray)
var valueInComparableArray: Element?
if let index = indexInComparableArray valueInComparableArray = comparableArray[index]
var valueInTestableArray: Element?
if let index = indexInComparableArray valueInTestableArray = testableArray[index]
XCTAssertEqual(valueInComparableArray, valueInTestableArray)
private func _testfindFirstIndex(comparableArray: Collection, testableArray: Collection)
let index1 = comparableArray.firstIndex(of: elementToSearch)
let index2 = testableArray.binarySearch(forFirstIndexOf: elementToSearch)
_testElementsAreEqual(indexInComparableArray: index1, comparableArray: comparableArray,
indexInTestableArray: index2, testableArray: testableArray)
class TestsInEmptyArray: XCTestCase, _BinarySearchTestable
var array: [String]!
var elementToSearch: String!
override func setUp()
array = []
elementToSearch = "value"
func testFindFirstIndexOfValueInCollection() _testFindFirstIndexOfValueInCollection()
func testFindLastIndexOfValueInCollection() _testFindLastIndexOfValueInCollection()
func testFindIndicesRangeOfValueInCollection() _testFindIndicesRangeOfValueInCollection()
class TestsInArray: XCTestCase, _BinarySearchTestable
var array: [Int]!
var elementToSearch: Int!
override func setUp()
array = [1, 2, 3, 3, 3, 3, 3, 4, 5, 5]
elementToSearch = 3
func testFindFirstIndexOfValueInCollection() _testFindFirstIndexOfValueInCollection()
func testFindLastIndexOfValueInCollection() _testFindLastIndexOfValueInCollection()
func testFindIndicesRangeOfValueInCollection() _testFindIndicesRangeOfValueInCollection()
class TestsInArrayWithOneElement: XCTestCase, _BinarySearchTestable
var array: [Date]!
var elementToSearch: Date!
override func setUp()
let date = Date()
array = [date]
elementToSearch = date
func testFindFirstIndexOfValueInCollection() _testFindFirstIndexOfValueInCollection()
func testFindLastIndexOfValueInCollection() _testFindLastIndexOfValueInCollection()
func testFindIndicesRangeOfValueInCollection() _testFindIndicesRangeOfValueInCollection()
【讨论】:
【参考方案6】:Swift 5 中的简单解决方案:
func binarySerach(list: [Int], item: Int) -> Int?
var low = 0
var high = list.count - 1
while low <= high
let mid = (low + high) / 2
let guess = list[mid]
if guess == item
return mid
else if guess > item
high = mid - 1
else
low = mid + 1
return nil
let myList = [1,3,4,7,9]
print(binarySerach(list: myList, item: 9))
//Optional(4)
【讨论】:
【参考方案7】:通过递归二分查找,
func binarySearch(data : [Int],search: Int,high : Int,low:Int) -> Int?
if (low > high)
return nil
let mid = low + (low + high)/2
if (data[mid] == search)
return mid
else if (search < data[mid])
return binarySearch(data: data, search: search, high: high-1, low: low)
else
return binarySearch(data: data, search: search, high: high, low: low+1)
输入:let arry = Array(0...5)
// [0,1,2,3,4,5]
print(binarySearch(data: arry, search: 0, high: arry.count-1, low: 0))
【讨论】:
【参考方案8】:这是我最喜欢的二分搜索实现。它不仅对查找元素很有用,而且对查找插入索引也很有用。通过提供相应的谓词(例如 $0 < x
vs $0 > x
vs $0 <= x
vs $0 >= x
)来控制关于假定排序顺序(升序或降序)和相等元素行为的详细信息。该评论明确地说明了它的作用。
extension RandomAccessCollection
/// Finds such index N that predicate is true for all elements up to
/// but not including the index N, and is false for all elements
/// starting with index N.
/// Behavior is undefined if there is no such N.
func binarySearch(predicate: (Element) -> Bool) -> Index
var low = startIndex
var high = endIndex
while low != high
let mid = index(low, offsetBy: distance(from: low, to: high)/2)
if predicate(self[mid])
low = index(after: mid)
else
high = mid
return low
示例用法:
(0 ..< 778).binarySearch $0 < 145 // 145
【讨论】:
一个 Swift 3 版本发布在***.com/questions/40226479/…。 在while循环前添加if low == high || predicate(self[index(high, offsetBy: -1)]) return high
,优化追加操作。
这应该是RandomAccessCollection
的扩展,而不是Collection
。这个扩展只能保证 O(n log n) 复杂度。
下标到 Collection 总是 O(1),但 K 偏移索引是 O(K)。因此,Collection 上此扩展的总体复杂度为 O(N)(因为 N/2 + N/4 + N/8 + ... + 1)。 RandomAccessCollection 的复杂度是 O(logN),因为偏移量是 O(1)。
@DanRosenstark taylorswift 绝对正确,该算法仅保证 RandomAccessCollection 的 O(logN) 复杂度,而不是任意集合。然而,造成这种情况的具体原因并不完全正确,所以我觉得我需要澄清它以避免在 cmets 中进一步混淆。【参考方案9】:
extension ArraySlice where Element: Comparable
func binarySearch(_ value: Element) -> Int?
guard !isEmpty else return nil
let midIndex = (startIndex + endIndex) / 2
if value == self[midIndex]
return midIndex
else if value > self[midIndex]
return self[(midIndex + 1)...].binarySearch(value)
else
return self[..<midIndex].binarySearch(value)
extension Array where Element: Comparable
func binarySearch(_ value: Element) -> Int?
return self[0...].binarySearch(value)
在我看来,这是非常易读的,它利用了 Swift 的 ArraySlice 是 Array 的一个视图,并且保留了与它共享存储的原始 Array 相同的索引,因此在没有突变的情况下(就像在这种情况下),因此非常有效。
【讨论】:
【参考方案10】:我在Indexable
上使用extension
来实现indexOfFirstObjectPassingTest
。
test
谓词,并返回第一个元素的索引 以通过测试。
如果没有这样的索引,则返回Indexable
中的endIndex
。
如果Indexable
为空,您将获得endIndex
。
示例
let a = [1,2,3,4]
a.map$0>=3
// returns [false, false, true, true]
a.indexOfFirstObjectPassingTest $0>=3
// returns 2
重要
您需要确保 test
永远不会在 false
中为任何索引返回它已表示 true
的索引。这相当于通常的前提条件,即二进制搜索要求您的数据有序。
具体来说,你不能这样做a.indexOfFirstObjectPassingTest $0==3
。这将无法正常工作。
为什么?
indexOfFirstObjectPassingTest
很有用,因为它可以让您查找数据范围。通过调整测试,你可以找到“stuff”的下限和上限。
这是一些数据:
let a = [1,1,1, 2,2,2,2, 3, 4, 5]
我们可以像这样在所有2
s 中找到Range
...
let firstOf2s = a.indexOfFirstObjectPassingTest($0>=2)
let endOf2s = a.indexOfFirstObjectPassingTest($0>2)
let rangeOf2s = firstOf2s..<endOf2s
如果数据中没有2
s,我们将返回一个空范围,我们不需要任何特殊处理。
只要有2
s,我们就会找到所有的。
例如,我在layoutAttributesForElementsInRect
的实现中使用它。我的UICollectionViewCells
存储在一个数组中垂直排序。很容易编写一对调用来查找特定矩形内的所有单元格并排除任何其他单元格。
代码
extension Indexable
func indexOfFirstObjectPassingTest( test: (Self._Element -> Bool) ) -> Self.Index
var searchRange = startIndex..<endIndex
while searchRange.count > 0
let testIndex: Index = searchRange.startIndex.advancedBy((searchRange.count-1) / 2)
let passesTest: Bool = test(self[testIndex])
if(searchRange.count == 1)
return passesTest ? searchRange.startIndex : endIndex
if(passesTest)
searchRange.endIndex = testIndex.advancedBy(1)
else
searchRange.startIndex = testIndex.advancedBy(1)
return endIndex
免责声明和警告
我有大约 6 年的 ios 经验,10 年的 Objective C 和 >18 的编程经验……
…但我在 Swift 的第 3 天 :-)
-
我在
Indexable
协议上使用了一个扩展。这可能是愚蠢的做法——欢迎提供反馈。
Binary searches 是 notoriously hard 以正确编码。您确实应该阅读该链接以了解其实施中的常见错误,但这里是摘录:
当 Jon Bentley 将其作为专业程序员课程中的一个问题时,他发现 90% 的人在经过几个小时的工作后未能正确编码二进制搜索,而另一项研究表明 准确的代码因为它只出现在二十本教科书中的五本中。此外,Bentley 自己在 1986 年出版的《Programming Pearls》一书中实现的二分搜索包含一个二十多年未被发现的错误。
鉴于最后一点,这里是此代码的测试。他们通过。它们不太可能是详尽无遗的——因此肯定仍然存在错误。不能保证测试实际上是正确的!测试没有测试。
测试
class BinarySearchTest: XCTestCase
func testCantFind()
XCTAssertEqual([].indexOfFirstObjectPassingTest (_: Int) -> Bool in false, 0)
XCTAssertEqual([1].indexOfFirstObjectPassingTest (_: Int) -> Bool in false, 1)
XCTAssertEqual([1,2].indexOfFirstObjectPassingTest (_: Int) -> Bool in false, 2)
XCTAssertEqual([1,2,3].indexOfFirstObjectPassingTest (_: Int) -> Bool in false, 3)
XCTAssertEqual([1,2,3,4].indexOfFirstObjectPassingTest (_: Int) -> Bool in false, 4)
func testAlwaysFirst()
XCTAssertEqual([].indexOfFirstObjectPassingTest (_: Int) -> Bool in true, 0)
XCTAssertEqual([1].indexOfFirstObjectPassingTest (_: Int) -> Bool in true, 0)
XCTAssertEqual([1,2].indexOfFirstObjectPassingTest (_: Int) -> Bool in true, 0)
XCTAssertEqual([1,2,3].indexOfFirstObjectPassingTest (_: Int) -> Bool in true, 0)
XCTAssertEqual([1,2,3,4].indexOfFirstObjectPassingTest (_: Int) -> Bool in true, 0)
func testFirstMatch()
XCTAssertEqual([1].indexOfFirstObjectPassingTest 1<=$0, 0)
XCTAssertEqual([0,1].indexOfFirstObjectPassingTest 1<=$0, 1)
XCTAssertEqual([1,2].indexOfFirstObjectPassingTest 1<=$0, 0)
XCTAssertEqual([0,1,2].indexOfFirstObjectPassingTest 1<=$0, 1)
func testLots()
let a = Array(0..<1000)
for i in a.indices
XCTAssertEqual(a.indexOfFirstObjectPassingTest(Int(i)<=$0), i)
【讨论】:
非常感谢您的详细回答和解释。我阅读了Indexable
的 swift 2.x 文档:“重要提示:在大多数情况下,最好忽略此协议并改用 CollectionType
,因为它具有更完整的界面。”因此,根据 Vadim 的回答,我使用了 extension CollectionType where Index: RandomAccessIndexType
。另外我想知道在while
之前订购(自我)是否值得和可能的傻瓜证明@
嗨@ajp,感谢您的想法。 1.我不太了解swift标准库(仍然!),但总的来说,我会使用支持所需功能的最小接口。 2. 我理解您的担忧,但我会建议您事先不要这样做。排序是(在大多数情况下)O(n.log(n))
操作。二分查找是O(log(n))
操作。如果您使用二进制搜索,您可能需要(巨大的)渐近性能差异。如果您不需要这种差异,则最好使用完全不同的算法来处理无序数据。
谢谢@Benjohn,明白了,很好的逻辑。
字符串版本怎么样?
@RajuyourPepe 该代码是通用的,因此它适用于任何已排序的集合,包括已排序的字符串。除非您的意思是在字符串中进行二进制搜索?这需要对字符进行排序,您也可以这样做,而且可能很有用。【参考方案11】:
这里是使用while语法的二分搜索
func binarySearch<T: Comparable>(_ a: [T], key: T) -> Int?
var lowerBound = 0
var upperBound = a.count
while lowerBound < upperBound
let midIndex = lowerBound + (upperBound - lowerBound) / 2
if a[midIndex] == key
return midIndex
else if a[midIndex] < key
lowerBound = midIndex + 1
else
upperBound = midIndex
return nil
【讨论】:
【参考方案12】:这是一个排序的字符串数组的实现。
var arr = ["a", "abc", "aabc", "aabbc", "aaabbbcc", "bacc", "bbcc", "bbbccc", "cb", "cbb", "cbbc", "d" , "defff", "deffz"]
func binarySearch(_ array: [String], value: String) -> String
var firstIndex = 0
var lastIndex = array.count - 1
var wordToFind = "Not founded"
var count = 0
while firstIndex <= lastIndex
count += 1
let middleIndex = (firstIndex + lastIndex) / 2
let middleValue = array[middleIndex]
if middleValue == value
wordToFind = middleValue
return wordToFind
if value.localizedCompare(middleValue) == ComparisonResult.orderedDescending
firstIndex = middleIndex + 1
if value.localizedCompare(middleValue) == ComparisonResult.orderedAscending
print(middleValue)
lastIndex = middleIndex - 1
return wordToFind
//print d
print(binarySearch(arr, value: "d"))
【讨论】:
【参考方案13】:这是一个完整的示例,其中包含 Swift 3.1 的几个测试用例。这不可能比默认实现更快,但这不是重点。数组扩展在底部:
// BinarySearchTests.swift
// Created by Dan Rosenstark on 3/27/17
import XCTest
@testable import SwiftAlgos
class BinarySearchTests: XCTestCase
let sortedArray : [Int] = [-25, 1, 2, 4, 6, 8, 10, 14, 15, 1000]
func test5()
let traditional = sortedArray.index(of: 5)
let newImplementation = sortedArray.indexUsingBinarySearch(of: 5)
XCTAssertEqual(traditional, newImplementation)
func testMembers()
for item in sortedArray
let traditional = sortedArray.index(of: item)
let newImplementation = sortedArray.indexUsingBinarySearch(of: item)
XCTAssertEqual(traditional, newImplementation)
func testMembersAndNonMembers()
for item in (-100...100)
let traditional = sortedArray.index(of: item)
let newImplementation = sortedArray.indexUsingBinarySearch(of: item)
XCTAssertEqual(traditional, newImplementation)
func testSingleMember()
let sortedArray = [50]
for item in (0...100)
let traditional = sortedArray.index(of: item)
let newImplementation = sortedArray.indexUsingBinarySearch(of: item)
XCTAssertEqual(traditional, newImplementation)
func testEmptyArray()
let sortedArray : [Int] = []
for item in (0...100)
let traditional = sortedArray.index(of: item)
let newImplementation = sortedArray.indexUsingBinarySearch(of: item)
XCTAssertEqual(traditional, newImplementation)
extension Array where Element : Comparable
// self must be a sorted Array
func indexUsingBinarySearch(of element: Element) -> Int?
guard self.count > 0 else return nil
return binarySearch(for: element, minIndex: 0, maxIndex: self.count - 1)
private func binarySearch(for element: Element, minIndex: Int, maxIndex: Int) -> Int?
let count = maxIndex - minIndex + 1
// if there are one or two elements, there is no futher recursion:
// stop and check one or both values (and return nil if neither)
if count == 1
return element == self[minIndex] ? minIndex : nil
else if count == 2
switch element
case self[minIndex]: return minIndex
case self[maxIndex]: return maxIndex
default: return nil
let breakPointIndex = Int(round(Double(maxIndex - minIndex) / 2.0)) + minIndex
let breakPoint = self[breakPointIndex]
let splitUp = (breakPoint < element)
let newMaxIndex : Int = splitUp ? maxIndex : breakPointIndex
let newMinIndex : Int = splitUp ? breakPointIndex : minIndex
return binarySearch(for: element, minIndex: newMinIndex, maxIndex: newMaxIndex)
这是相当自制的,所以...告诫购买者。它确实有效并且确实进行了二进制搜索。
【讨论】:
【参考方案14】:这是一个更好的实现,如果数组中有多个索引,则返回多个索引。
extension Array where Element: Comparable
/* Array Must be sorted */
func binarySearch(key: Element) -> [Index]?
return self.binarySearch(key, initialIndex: 0)
private func binarySearch(key: Element, initialIndex: Index) -> [Index]?
guard count > 0 else return nil
let midIndex = count / 2
let midElement = self[midIndex]
if key == midElement
// Found!
let foundIndex = initialIndex + midIndex
var indexes = [foundIndex]
// Check neighbors for same values
// Check Left Side
var leftIndex = midIndex - 1
while leftIndex >= 0
//While there is still more items on the left to check
print(leftIndex)
if self[leftIndex] == key
//If the items on the left is still matching key
indexes.append(leftIndex + initialIndex)
leftIndex--
else
// The item on the left is not identical to key
break
// Check Right side
var rightIndex = midIndex + 1
while rightIndex < count
//While there is still more items on the left to check
if self[rightIndex] == key
//If the items on the left is still matching key
indexes.append(rightIndex + initialIndex)
rightIndex++
else
// The item on the left is not identical to key
break
return indexes.sort return $0 < $1
if count == 1
guard let first = first else return nil
if first == key
return [initialIndex]
return nil
if key < midElement
return Array(self[0..<midIndex]).binarySearch(key, initialIndex: initialIndex + 0)
if key > midElement
return Array(self[midIndex..<count]).binarySearch(key, initialIndex: initialIndex + midIndex)
return nil
【讨论】:
以上是关于Swift:标准数组的二进制搜索?的主要内容,如果未能解决你的问题,请参考以下文章