今天我做了一份工作测试,并被要求搜索一个整数数组,这是一个问题:
本练习的目的是检查数组中数字的存在。
规格:
这些项是按升序排列的整数。
阵列最多可包含一百万个项目
实现功能existInArray(_numbers:[Int],_ k:Int),以便如果k属于数字,则返回true,否则函数应返回false。
例:
let numbers = [-9, 14, 37, 102] existsInArray(numbers, 102) // returns true existsInArray(numbers, 36) //returns false
注意:尝试节省CPU周期
好吧,所以我给了我下面的代码,然后等待结果
func existsInArray(_ numbers: [Int], _ k: Int) -> Bool {
if numbers.isEmpty {
return false
}
let numbersHalfIndex: Int = (numbers.count/2)
if k == numbers[numbersHalfIndex] {
return true
} else if k != numbers[0] && numbers.count == 1 {
return false
} else if k <= numbers[numbersHalfIndex] {
let leftHalfNumbersArray = numbers[0 ..< numbersHalfIndex]
return existsInArray(Array(leftHalfNumbersArray), k)
} else if k > numbers[numbersHalfIndex] {
let rightHalfNumbersArray = numbers[numbersHalfIndex ..< numbers.count]
return existsInArray(Array(rightHalfNumbersArray), k)
} else {
return false
}
}
原来,“该解决方案无法在合理的时间内处理一百万个项目”,现在我不知道自己做错了什么,因为二进制搜索的速度与f * ck一样快。
我唯一的猜测是,也许number.count或numbers [0 ... <numbersHalfIndex]或numbers [numbersHalfIndex ... <number.count]会使一切变得比预期的慢。
我在绊倒吗?
编辑:如果有人好奇,我测试了我的代码和Martin R代码,以了解使用ArraySlice在时间方面的影响。我使用从0开始以升序排列的100.000.000 iten数组。这是我捕获时间的方式:
print("////////// MINE //////////")
var startTime = CFAbsoluteTimeGetCurrent()
print(existsInArray(numbers, 0))
var timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for mine: \(timeElapsed) s.")
print("////////// Martin R //////////")
counter = 0
startTime = CFAbsoluteTimeGetCurrent()
print(existsInArrayOptimal(numbers, 0))
timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for Martin R: \(timeElapsed) s.")
结果如下:
////////// MINE ///////////
真正
我的时间:
1.2008800506591797秒
/////////// Martin R ///////////
真正
Martin R经过的时间:0.00012993812561035156 s。
快约1000倍!
访问number.count
不是问题,因为这是数组的O(1)操作。切片numbers[0 ...< numbersHalfIndex]
也不是问题。但是Array(leftHalfNumbersArray)
从切片创建一个新数组,并复制所有元素。
有两种方法可以避免这种情况:
第二种方法的演示:
func existsInArray(_ numbers: ArraySlice<Int>, _ k: Int) -> Bool {
if numbers.isEmpty {
return false
}
let numbersHalfIndex = numbers.startIndex + numbers.count / 2
if k == numbers[numbersHalfIndex] {
return true
} else if k < numbers[numbersHalfIndex] {
return existsInArray(numbers[..<numbersHalfIndex], k)
} else {
return existsInArray(numbers[(numbersHalfIndex + 1)...], k)
}
}
请注意,数组切片与原始数组共享索引,因此索引不必从零开始。这numbers.startIndex
就是用于索引计算的原因。
还有一个包装函数,它需要一个“真实的”数组参数:
func existsInArray(_ numbers: [Int], _ k: Int) -> Bool {
return existsInArray(numbers[...], k)
}
正如@Leo建议的那样,您可以将其实现为一个收集方法,而不是实现两个单独的方法。集合索引不一定是整数,但是对于a而言RandomAccessCollection
,索引计算保证为O(1)。您也可以将其概括为任意可比较元素的集合,而不是整数。
这是一个可能的实现:
extension RandomAccessCollection where Element: Comparable {
/// Returns a Boolean value indicating whether the collection contains the
/// given element. It is assumed that the collection elements are sorted
/// in ascending (non-decreasing) order.
///
/// - Parameter element: The element to find in the collection.
/// - Returns: `true` if the element was found in the collection; otherwise,
/// `false`.
///
/// - Complexity: O(log(*n*)), where *n* is the size of the collection.
func binarySearch(for element: Element) -> Bool {
if isEmpty {
return false
}
let midIndex = index(startIndex, offsetBy: count / 2)
if element == self[midIndex] {
return true
} else if element < self[midIndex] {
return self[..<midIndex].binarySearch(for: element)
} else {
return self[index(after: midIndex)...].binarySearch(for: element)
}
}
}
用法:
let numbers = [-9, 14, 37, 102]
print(numbers.binarySearch(for: 102)) // true
print(numbers.binarySearch(for: 36)) // false
另外,一种非递归方法可更新搜索范围的索引:
extension RandomAccessCollection where Element: Comparable {
func binarySearch(for element: Element) -> Bool {
var lo = startIndex
var hi = endIndex
while lo < hi {
let mid = index(lo, offsetBy: distance(from: lo, to: hi) / 2)
if element == self[mid] {
return true
} else if element < self[mid] {
hi = mid
} else {
lo = index(after: mid)
}
}
return false
}
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句