携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情 。如果哪里写的不对,请大家评论批评。
什么是堆
堆是一种特殊的完全二叉树,他的任何一个节点都大于或者等于他的左右子节点,这种被称为最大堆,可推理根节点是最大的值。如果他的任何一个节点都小于或者等于他的左右子节点,这种被称为最小堆。可推理根节点是最小值。
如图:(红色块是数据的小标)
堆排序
堆排序是利用堆这种数据结构设计的一种算法,经过一系列的建堆,创建最小堆,或者最大堆就可以得到一个有顺序的值。
如图:
节点的规律
- 左边的子节点所在位置下标是2*i+1
- 右边的子节点所在位置下标是2*i+2
- 两者的父节点所在位置下表是
(i-1) / 2
分析
- 数据建堆,无序的堆序列
- 最大堆:将每个父节点和左右的子节点进行比较,将大的子节点和父节点做交换,直到根节点成为最大的数值,变成最大堆
- 最小堆:将每个父节点和左右的子节点进行比较,将小的子节点和父节点做交换,直到根节点成为最小的数值,变成最小堆
- 将堆顶的最小值或者最大值和二叉树最后一个节点的值交换,然后提取出最大值或者最小值。数据减少一位
- 重复2、3、4的过程,直到只剩下一个堆顶
图解
以上述数据为例【2,3,8,1,4,9,10,7】 下面按照最大堆进行排序
已经过程就生成了最大堆,按照上述的分析,重复这个过程就可以进行排序,看下图
- 首先提出堆顶的最大值与数据最后一位交换,保持最后一位不变化。
- 然后依次看下标2的数据为9,其左右子节点分别是2,8保持不变,
- 然后看下标为1的数据为7,其左右子节点分别是3,4保持不变,
- 最后看下标为0的数据为1,其右子节点数据最大,交换位置,下标0为9,下标2为1,
- 这时候还没结束,还要继续比较下标为2的数据与其子节点的数据,发现下标为6的数据大于当前数据,再次进行交换.
- 这里我们就得到了另一个最大堆。
(ps):下标为7的数据,存放了我们的最大值,所以不参与比较,在代码中,长度变成了array.count - 1
此时我们又得到了一个最大堆,提取最大值。又开始了循环,下面不做文字介绍了,看图吧
代码Swift
// 堆排序
func adjustHeap(_ array : inout Array<Int>, _ i : Int, _ length : Int){
// 当前节点,暂定最大数据
var lar = i
// 左孩子节点
let left = i * 2 + 1
// 右孩子节点
let right = i * 2 + 2
// 如果左孩子节点存在 && 数据大于当前最大数据 则记录下标数据最大
if left < length && array[left] > array[lar] {
lar = left
}
// 如果右孩子节点存在 && 数据大于当前最大数据 则记录下标数据最大
if right < length && array[lar] < array[right] {
lar = right
}
// 如果最大数据坐标有变化, 则去替换数据
if lar != i {
array.swapAt(lar, i)
// 替换数据之后,如果当前下标也是父节点,需要继续和子节点比较
adjustHeap(&array, lar, length)
}
}
func heapSort(_ array :inout Array<Int>){
// 建堆
for i in (0...((array.count - 1) / 2)).reversed() {
adjustHeap(&array, i, array.count)
}
// 排序
for j in (1...(array.count-1)).reversed() {
//将堆顶元素与末尾元素进行交换
array.swapAt(0, j)
//重新对堆进行调整
adjustHeap(&array, 0, j)
}
}
var list : Array<Int> = [2, 3, 8, 1, 4, 9, 10, 7, 16, 14,111,231,51412,3125,12,13]
heapSort(&list)
print(list)