您的位置:首页 > 移动开发 > Swift

《算法》排序第一天(选择排序、插入排序、希尔排序)

2017-07-25 00:00 141 查看
摘要: 过完愉快(热死人)的周末之后本应该继续开始学习的,但有些别的事情耽误了一下。
之前看了动态连通性,这次开始学习新的东西--排序。

今天又小小的感受了一下算法的强大之处。

上一篇文章我们跟随书中的思路将动态连通性的问题在10万个触点的计算从280多秒(quick-find)优化到了只有不到0.05秒(quick-union),第一次让我感受到了算法的强大。

今天,继续跟随书中思路通过对排序算法的学习。这次只看到了排序算法的前三个算法选择排序、插入排序和希尔排序。这次将继续使用swift按照书中的思路实现这三个算法,并最后做一个不负责任的小测试(今天使用的是随机的数组测试排序算法的大致性能,并不会做特别详细严谨的测试)。

今天我们做了一个结构体来完成这次排序算法的学习。

/// 排序
struct Sort {

public var array:[Int]

/// 比较数组array中对应角标的大小a<b true  a>=b false
///
/// - Parameters:
///   - a: array数组角标
///   - b: array数组角标
/// - Returns: 比较结果
public func less(a:Int,b:Int)->Bool {
return  array[a]<array
}

/// 数组对应角标交换位置
///
/// - Parameters:
///   - a: 角标a
///   - b: 角标b
public mutating func exch(a:Int,b:Int)->Void {
var c:Int?
c = array[a]
array[a] = array[b]
array[b] = c!
}

/// 打印数组array
public func show()->Void {
print(array)
}

}

之所以选择结构体也是为了加深对结构体的学习。

这个结构体我们声明了一个数组array,我们今天的排序就是针对这个数组的排序;

为了方便排序我们还提供了比较两个角标元素大小的less方法和交换两个角标元素的exch方法还有查看数组的show方法。

好了完事具备,开始学习吧!!

※关于 mutating 意思就是枚举和结构体的方法要改变自己内部的值就需要加上这个关键字。参考这里

选择排序:

选择排序的思路很简单。首先找到数组中最小的元素,然后将这个最小的元素和数组第一个元素交换。其次,找到剩下的元素中最小的,将它和第二个元素交换。如此往复,直到将整个数组完成排序。因为在一直选择剩余元素中的最小者所以叫[b]选择排序
。<-----书上是这么说的。

那么按照这个思路我们就用swift给我们的Sort结构体做一个实现选择排序的方法吧。

/// 选择排序
public mutating func selectedSort()->Void {

for i in 0..<array.count {
///假设第一个即为最小的
var min = i
for j in i+1..<array.count {
if array[j]<array[min] {
min = j
}
}
exch(a: i, b: min)
}
}

选择排序是一个很容易理解和实现的简单排序算法,它有两个很鲜明的特点:1、运行时间和数组是否有序无关。数据交换和数据比较的次数都是根据数组元素的个数来确定了,元素个数相同的有序数组并不会比无序数组少执行一步。2、数据交换次数少。根据最外层的for循环我们可以看到,数据交换的次数与数组元素的个数一样。

为什么[1,2,3,4,5]和[5,4,3,2,1]一样?我的是有序的,我这个就要更快!!

插入排序:

人们在打扑克的时候都习惯将新抓到的牌插入到已经排好的牌面中的适当的位置。在计算机实现中,为了给新插入的元素腾出空间就需要之后的元素都右移以为。这种算法就叫插入算法。<---还是书上说的。:smile:


图片很烂,以后会换。

按照这个思路开始实现吧。(其实是按照书中的代码改成的swift。。。)

/// 插入排序
public mutating func insertSort()->Void {

for i in 1..<array.count {
for j in 0..<i {
/**
这里我就想说,swfit 怎么能快速的做反向循环啊?
这个不支持C语言风格的 for 循环很蛋疼啊。
*/
let num = i-j
if less(a: num, b: num-1) {
break
}else {
exch(a: num, b: num-1)
}
}
}
}

根据上面面图示可以看出插入排序在处理完全无序的数组时需要进行大量的比较和交换操作,有序或部分有序的数组数据交换则会很少。这就使得插入排序算法更适合处理有序或者部分有序的数据。



选择和插入排序的可视化比较

作为一名iOS开发选择排序和插入排序已经能够完成我们的需求,毕竟手机处理的数据有个一两百条已经算是很多了。但是当处理大量的数据时这两个排序算法就显得力不从心了,特别是在处理大量的无序数据时插入排序更是不要不要的。虽然选择排序执行的数据交换很少,但是仍然需要执行很多次的比较操作,而且不管数据是否有序。

希尔排序:

希尔排序是一种基于插入排序的快速排序算法。对于大规模乱序数组插入排序很慢,是因为一次只能交换相邻的两个元素,元素只能一点点的从数组的一端移动到另一端。希尔排序为了加快速度简单的改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。

希尔排序:

///希尔排序
public mutating func shellSort()-> Void {
let N = array.count;
var h = 1;
///
while h<N/3 {
h = 3*h+1;
}
print("H-->",h)
while h>=1 {
for i in h..<N {
var j = i
while j>=h && less(a: j, b: j-h) {
exch(a: j, b: j-h)
j = j-h
}
}
h = h/3
print("H-->",h)
}
}

希尔排序将整个数组按照间距h分为若干个数组并排序,将h递减。当h等于1时数组已经局部有序按照插入算法排序。希尔排序可以是单个元素一次移动到较远的位置这就减少了插入排序中元素交换的次数。随着h的递减数组逐渐变成局部有序的数组。最后对数组进行插入排序时数组已经局部有序,插入算法对局部有序的数组排序效率较高,由此提升插入排序的效率。

为了对三个排序算法的性能有一个大致的了解,我们生成了三个十万个随机元素的数组,分别使用三种算法进行排序。并分别记录开始和结束的时间作为参考。

func creatArray(N:Int)->[Int] {
var array = [Int]()
for _ in 0..<N {
let num = arc4random_uniform(UInt32(N))
array.append(Int(num))
}
return array
}

let array = NSArray(array: creatArray(N: 100000))
var ss = Sort(array: array1 as! [Int])




虽然这次小实验对插入排序很不公平但是更能代表排序的一般情况。

我们发现插入排序在处理完全无序的数据时性能跟选择排序有很大的差距。而希尔排序,也再次让我体会到了算法的强大之处。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐