排序算法(一) —— 堆排序之一个简单示例(一)

版本记录

版本号 时间
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())

后记

本篇主要讲述了堆排序,感兴趣的给个赞或者关注~~~

你可能感兴趣的:(排序算法(一) —— 堆排序之一个简单示例(一))