本小节主要是经过泛型二分法
,序列随机排列
,判断子序列
的位置的算法的几个Demo,来说了一些应该注意的知识点。git
本小节代码量有些多,不要求所有掌握。通读一遍便可
,之后在实际开发中有遇到算法相关的泛型封装能够回来看看~github
书中先对RandomAccessCollection协议
随机存取协议(笔记第三章第五节有讲,忘了的同窗能够回头看看) 封装一个二分法的extension方法,算法
extension RandomAccessCollection where Index == Int, IndexDistance == Int{只知足Int的二分查找
public func binarySearch(for value: Iterator.Element,
areInIncreasingOrder: (Iterator.Element, Iterator.Element) -> Bool)
-> Index? {
var left = 0
var right = count - 1
while left <= right {
let mid = (left + right) / 2
let candidate = self[mid]
if areInIncreasingOrder(candidate, value) {
left = mid + 1
}else if areInIncreasingOrder(value, candidate) {
right = mid - 1
}else {只有左右两个相等才会返回中间值
return mid
}
}
return nil
}
}
let binaryInt = [1,3,2,6,4]
let bin = binaryInt[1..<3]
bin.binarySearch(for: 2, areInIncreasingOrder: <)
print(binaryInt ?? 000)
复制代码
但会引入一个严重的bug
,并非全部的集合都以整数为索引的,Dictionary、Set、String都有本身的索引类型。
还有一个是以整数为索引的并不必定都以0开始
例如 Array[3..<5] 的startIndex就是3 ,若是使用就会在运行时崩溃
。 为此咱们进行二次修改dom
咱们二次修改上面的方法,来知足泛型的要求优化
泛型二分查找
extension RandomAccessCollection {
public func binarySearch(for value: Iterator.Element,
areInIncreasingOrder: (Iterator.Element, Iterator.Element) -> Bool)
-> Index? {
guard !isEmpty else {
return nil
}
/ left和right再也不是整数类型了,而是对于的索引值
var left = startIndex
var right = index(before: endIndex)
while left <= right {
let dist = distance(from: left, to: right) 计算left到right的距离
let mid = index(left, offsetBy: dist/2)
let candidate = self[mid]
if areInIncreasingOrder(candidate, value) {
left = index(after: mid)
}else if areInIncreasingOrder(value, candidate) {
right = index(before: mid)
}else {
return mid
}
}
return nil
}
}
extension RandomAccessCollection where Iterator.Element: Comparable {
func binarySearch(for value: Iterator.Element) -> Index? {
return binarySearch(for: value, areInIncreasingOrder: <)
}
}
let a = ["a", "c", "d", "g"]
let r = a.reversed()
r.binarySearch(for: "d", areInIncreasingOrder: >) == r.startIndex
let s = a[1..<3]
s.startIndex
s.binarySearch(for: "d")
复制代码
这样对二分法的泛型改造就结束啦~spa
咱们先用代码实现一下 Fisher-Yates洗牌算法。code
/集合随机排列
extension Array {
mutating func shuffle(){
for i in 0..<(count - 1) {
let j = Int(arc4random_uniform(UInt32(count - i))) + i
guard i != j else {保证不会将一个元素与本身交换
continue
}
self.swapAt(i, j)将两个元素交换
}
}
func shuffled() -> [Element] {不可变版本
var clone = self
clone.shuffle()
return clone
}
}
复制代码
和前面的二分法算法同样,这里仍是依赖整数索引
才能使用。 咱们用泛型进行二次改造orm
extension MutableCollection where Self: RandomAccessCollection {
mutating func shuffle() {
var i = startIndex
let beforeEndIndex = index(before: endIndex)
while i < beforeEndIndex {
let dist = distance(from: i, to: endIndex)
let randomDistance = IndexDistance.distance(dist)
let j = index(i, offsetBy: randomDistance)
guard i != else {
continue
}
swap(&self[i], &self[j]
formIndex(after: &i))
}
}
}
///二次封装
extension Sequence {
func shuffled() -> [Iterator.Element] {
var clone = Array(self)
clone.shuffle()
return clone
}
}
复制代码
这一小结最后一个Demo。。求子集合的位置。索引
extension Collection where Iterator.Element: Equatable {
func search<Other: Sequence>(for pattern: Other) -> Index?
where Other.Iterator.Element == Iterator.Element {
return indices.first(where: { (idx) -> Bool in
suffix(from: idx).starts(with: pattern)
})
}
}
let text = "it was the best of times"
text.search(for: ["w","a","s"])
复制代码
注:若是知道被搜索的序列和待搜索的序列都知足随机存取(RandomAccessCollection)的索引,咱们能够二次优化。ip
extension RandomAccessCollection
where Iterator.Element: Equatable,
Indices.Iterator.Element == Index,
SubSequence.Iterator.Element == Iterator.Element,
SubSequence.Indices.Iterator.Element == Index {
func search<Other: RandomAccessCollection>(for pattern: Other) -> Index?
where Other.IndexDistance == IndexDistance,
Other.Iterator.Element == Iterator.Element{
///若是匹配集合比原集合长 直接退出
guard !isEmpty && pattern.count <= count else {
return nil
}
///不然能够取到容纳匹配模式的最后一个索引。
let stopSearchIndex = index(endIndex, offsetBy: -pattern.count)
///检查从当前位置切片是否能够以匹配模式开头。
return prefix(upTo: stopSearchIndex).indices.first(where: { (idx) -> Bool in
suffix(from: idx).starts(with: pattern)
})
}
}
let numbers = 1..<100
numbers.search(for: 80..<90)
复制代码
overover~