版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.08.22 |
前言
排序算法是最常见的算法,其中包括了冒泡、选择等很多不同的排序算法,接下来几篇就会介绍相应的排序算法,其实前面几篇已经有所涉及了,以后有些东西我会慢慢移动和增加到这个专题里面。
开始
首先看一下本文写作环境。
写作环境:Swift 4, iOS 11, Xcode 9
Heapsort
是另一种基于比较的算法,它使用堆按升序对数组进行排序。 根据定义,Heapsort
利用了一个堆,它具有以下特性的部分排序的二叉树:
- 在最大堆中,所有父节点都大于其子节点。
- 在最小堆中,所有父节点都小于其子节点。
下图显示了父节点值带下划线的堆:
原理
对于任何给定的未排序数组,要从最低到最高排序,堆排序必须首先将此数组转换为最大堆。
通过筛选所有父节点来完成此转换,使它们最终位于正确的位置。 生成的最大堆是:
其中对应以下数组:
由于单个筛选操作的时间复杂度为O(log n)
,因此构建堆的总时间复杂度为O(n log n)
。
我们来看看如何按升序对此数组进行排序。
因为最大堆中的最大元素始终位于根,所以首先将索引0
处的第一个元素与索引n - 1
处的最后一个元素进行交换。作为此交换的结果,数组的最后一个元素位于正确的位置,但堆现在无效。 因此,下一步是将新的根节点5向下筛,直到它落在正确的位置。
请注意,您排除堆的最后一个元素,因为我们不再将其视为堆的一部分,而是排好序的数组。
作为筛选5的结果,第二大元素21成为新根。 您现在可以重复前面的步骤,使用最后一个元素6交换21,缩小堆并向下筛选6。
堆排序非常简单。 当你交换第一个和最后一个元素时,较大的元素以正确的顺序进入数组的后面。 您只需重复交换和筛选步骤,直到达到大小为1的堆。然后对阵列进行完全排序。
Implementation - 具体实现
接下来,您将实现此排序算法。 实际的实现非常简单,因为siftDown
方法已经完成了繁重的工作:
extension Heap {
func sorted() -> [Element] {
var heap = Heap(sort: sort, elements: elements) // 1
for index in heap.elements.indices.reversed() { // 2
heap.elements.swapAt(0, index) // 3
heap.siftDown(from: 0, upTo: index) // 4
}
return heap.elements
}
}
下面进行详细说明:
- 1)您首先制作堆的副本。 堆排序对元素数组进行排序后,它不再是有效的堆。 通过处理堆的副本,可以确保堆保持有效。
- 2)从最后一个元素开始循环遍历数组。
- 3)您交换第一个元素和最后一个元素。 这会将最大的未排序元素移动到其正确的位置。
- 4)由于堆现在无效,因此必须对新的根节点进行筛选。 结果,下一个最大的元素将成为新的根。
请注意,为了支持堆排序,您已向siftDown
方法添加了一个额外的参数upTo
。 这样,sift down
仅使用数组的未排序部分,该部分随着循环的每次迭代而缩小。
最后,尝试一下您的新方法:
let heap = Heap(sort: >, elements: [6, 12, 2, 26, 8, 18, 21, 9, 5])
print(heap.sorted())
看一下输出打印
[2, 5, 6, 8, 9, 12, 18, 21, 26]
Performance - 性能
即使您获得了内存中排序的好处,堆排序的性能也是O(n log n)
的最佳,更差和平均情况。 这是因为您必须遍历整个列表一次,并且每次交换元素时都必须执行向下筛选,这是一个O(log n)
操作。
堆排序也不是一种稳定的排序,因为它取决于元素的布局方式和放入堆中的方式。
源码
下面我们就一起看一下源码。
struct Heap {
var elements: [Element] = []
let sort: (Element, Element) -> Bool
init(sort: @escaping (Element, Element) -> Bool, elements: [Element] = []) {
self.sort = sort
self.elements = elements
if !elements.isEmpty {
for i in stride(from: elements.count / 2 - 1, through: 0, by: -1) {
siftDown(from: i, upTo: elements.count)
}
}
}
var isEmpty: Bool {
return elements.isEmpty
}
var count: Int {
return elements.count
}
func peek() -> Element? {
return elements.first
}
func leftChildIndex(ofParentAt index: Int) -> Int {
return (2 * index) + 1
}
func rightChildIndex(ofParentAt index: Int) -> Int {
return (2 * index) + 2
}
func parentIndex(ofChildAt index: Int) -> Int {
return (index - 1) / 2
}
mutating func remove() -> Element? {
guard !isEmpty else {
return nil
}
elements.swapAt(0, count - 1)
defer {
siftDown(from: 0, upTo: elements.count)
}
return elements.removeLast()
}
mutating func siftDown(from index: Int, upTo size: Int) {
var parent = index
while true {
let left = leftChildIndex(ofParentAt: parent)
let right = rightChildIndex(ofParentAt: parent)
var candidate = parent
if left < size && sort(elements[left], elements[candidate]) {
candidate = left
}
if right < size && sort(elements[right], elements[candidate]) {
candidate = right
}
if candidate == parent {
return
}
elements.swapAt(parent, candidate)
parent = candidate
}
}
mutating func insert(_ element: Element) {
elements.append(element)
siftUp(from: elements.count - 1)
}
mutating func siftUp(from index: Int) {
var child = index
var parent = parentIndex(ofChildAt: child)
while child > 0 && sort(elements[child], elements[parent]) {
elements.swapAt(child, parent)
child = parent
parent = parentIndex(ofChildAt: child)
}
}
mutating func remove(at index: Int) -> Element? {
guard index < elements.count else {
return nil // 1
}
if index == elements.count - 1 {
return elements.removeLast() // 2
} else {
elements.swapAt(index, elements.count - 1) // 3
defer {
siftDown(from: index, upTo: elements.count) // 5
siftUp(from: index)
}
return elements.removeLast() // 4
}
}
func index(of element: Element, startingAt i: Int) -> Int? {
if i >= count {
return nil
}
if sort(element, elements[i]) {
return nil
}
if element == elements[i] {
return i
}
if let j = index(of: element, startingAt: leftChildIndex(ofParentAt: i)) {
return j
}
if let j = index(of: element, startingAt: rightChildIndex(ofParentAt: i)) {
return j
}
return nil
}
}
extension Heap {
func sorted() -> [Element] {
var heap = Heap(sort: sort, elements: elements)
for index in heap.elements.indices.reversed() {
heap.elements.swapAt(0, index)
heap.siftDown(from: 0, upTo: index)
}
return heap.elements
}
}
let heap = Heap(sort: >, elements: [6, 12, 2, 26, 8, 18, 21, 9, 5])
print(heap.sorted())
后记
本篇主要讲述了堆排序,感兴趣的给个赞或者关注~~~