(WWDC) 拥抱算法 (Embracing Algorithms) 下



在阅读此文前,请先阅读 拥抱算法 (Embracing Algorithms) 上 。



在数组中移动元素



(WWDC) 拥抱算法 (Embracing Algorithms) 下_第1张图片
将图层移动到最前面

如果你要将图中选中的图形移动到最前面,你会如何编写你的逻辑代码?



(WWDC) 拥抱算法 (Embracing Algorithms) 下_第2张图片
将元素移动到最前面

很难过,这个算法复杂度太高!

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第3张图片
复杂度太高的算法




stablePartition(isSuffixElement:) 方法可以满足要求,该方法的时间复杂度为O(nlogn)

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第4张图片
用stablePartition(isSuffixElement:)方法即可
(WWDC) 拥抱算法 (Embracing Algorithms) 下_第5张图片
stablePartition(isSuffixElement:)的文档



时间复杂度为O(logn),这是个怎样的规模?
请注意,这里的logn是以2为底n的对数,即 log2n
一般来说,复杂度中含有logn的算法都使用了二分法。

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第6张图片
O(n) 和 O(logn) 的对比



然后,再来观察时间复杂度为O(nlogn)又是一个怎样的规模。

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第7张图片
O(n) 和 O(nlogn) 的对比






再来看另一个例子

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第8张图片
bringForward()

这个方法的作用是依次移动选中的图形元素,使这些元素聚集到选中的最前面的那个元素的前一个位置处,并使这些被移动的元素保持原有的相对位置。除此之外,还要保证其他未选中的元素的相对位置不改变。



请看效果图:

移动前

移动后



然后,仔细观察下面的图片:

移动前
移动前

你是否想起了前面用到的stablePartition算法?




现在来改写这个方法的内部实现算法!

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第9张图片
改写算法

首先,获取到符合条件的首个元素的前一个位置。

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第10张图片
切片操作

然后通过切片,获取你真正需要操作的那部分元素。

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第11张图片
移动未选中的元素到末尾

最后,移动未选中的元素到数组末尾,并保持这些元素的相对位置不变。
Okay, well done!



需要注意,这里的shapes[predecessor...]的类型为ArraySlice
你可以理解为,这个切片就是对原始数组的引用。
所以,这里stablePartition方法可以通过切片来修改原始数组的值。




封装你的算法



一般来说,我们都会倾向于封装常用的算法,通过提高代码的复用率来减少工作量。
比如我们经常会用到快速排序算法,在Swift标准库中就集成了Collection类型中的sorted(by areInIncreasingOrder: (Element, Element) -> Bool)方法。
你需要做的就是给这个方法传入一个谓词函数(predicate)来决定元素的排序顺序。



现在,我们来封装Canvas类专用的算法,然后使其抽象并可用于所有的MutableCollection

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第12张图片

首先,将扩展的类改为Array,并将元素类型指定为Shape。

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第13张图片

然后,去掉shapes数组的调用,改为self

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第14张图片

接下来,让传入的谓词函数来决定元素的排序顺序。

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第15张图片

然后,更改类型为MutableCollection,并去掉对于元素类型的限制。

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第16张图片

有编译错误?别紧张!
千万不要像这样操作。

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第17张图片

其实,可以将predecessor的获取方法独立出来,而且抽象化。

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第18张图片

到这里,这个算法的封装完成了吗?

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第19张图片

你还需要为你的方法加上必要的注释,以便后续的使用。
毕竟,被抽象后的算法不再针对性地去解决某个具体的问题,而是解决某一类问题。
所以,你要讲清楚该算法的用途。
而且,你需要注明算法的时间复杂度,方便调用者评估算法的整体性能。

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第20张图片
(WWDC) 拥抱算法 (Embracing Algorithms) 下_第21张图片



通过Xcode,可以很方便地查看这些方法的文档!

(WWDC) 拥抱算法 (Embracing Algorithms) 下_第22张图片



以下为stablePartition方法的源码,可以看到二分法的使用:

extension MutableCollection {
    /// Moves all elements satisfying `isSuffixElement` into a suffix of the collection,
    /// preserving their relative order, returning the start of the resulting suffix.
    ///
    /// - Complexity: O(n log n) where n is the number of elements.
    /// - Precondition: `n == self.count`
    mutating func stablePartition(count n: Int, isSuffixElement: (Element) -> Bool) -> Index {
        if n == 0 { return startIndex }
        if n == 1 { return isSuffixElement(self[startIndex]) ? startIndex : endIndex }
        let h = n / 2, i = index(startIndex, offsetBy: h)
        let j = self[..

至于rotate(shiftingToStart:)方法的源码,可以在Swift Algorithms Prototype 中查看。

原文中的最后还有一个例子,也是关于stablePartition方法的使用,有兴趣请自行查看参考文章。






参考文章:
Embracing Algorithms




转载请注明出处,谢谢~

你可能感兴趣的:((WWDC) 拥抱算法 (Embracing Algorithms) 下)