本小節主要是經過泛型二分法
,序列隨機排列
,判斷子序列
的位置的算法的幾個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~