堆排序
一、堆结构
1. 堆的定义
堆中某个节点的值总是大于等于其子节点的值,并且堆是一颗完全二叉树。
堆可以用数组来表示,这是因为堆是完全二叉树,而完全二叉树很容易就存储在数组中。位置 k 的节点的父节点位置为 k/2,
而它的两个子节点的位置分别为 2k 和 2k+1。
这里不使用数组索引为 0 的位置,是为了更清晰地描述节点的位置关系。
1 | |
2. 上浮和下沉操作
在堆中,当一个节点比父节点大,那么需要交换这个两个节点。交换后还可能比它新的父节点大,因此需要不断地进行比较和交换操作,把这种操作称为上浮。
1 | |
类似地,当一个节点比子节点来得小,也需要不断地向下进行比较和交换操作,把这种操作称为下沉。一个节点如果有两个子节点,
应当与两个子节点中最大那个节点进行交换。
1 | |
3. 插入元素
将新元素放到数组末尾,然后上浮到合适位置
1 | |
4. 删除堆顶元素
从数组顶端删除最大元素,并将数组的最后一个元素放到顶端,并让这个元素下沉到合适的位置
1 | |
二、堆排序
给定一个数组,将其使用堆排序进行排序。需要先使用sink函数将这个数组构造成最大堆,接下来每次讲堆顶的最大元素取下来与数组的最后一个元素交换,这时候最大堆的结构已经被破坏,将数组元素个数减一并且对新的堆顶元素调用sink函数使数组恢复最大堆,重复执行这个过程,直到所有元素都已排序。
1. 构建堆
无序数组建立堆最直接的方法就是从左到右遍历数组进行上浮操作。
一个更高效的方法是从右至左进行下沉操作,如果一个节点的两个节点都已经是堆有序,那么进行下沉操作可以使得这个节点为
根节点的堆有序。叶子节点不需要进行下沉操作,可以忽略叶子节点的元素,因此只需要便利一半的元素即可。
2. 交换堆顶元素与最后一个元素
交换之后需要进行下沉操作维持堆的有序状态
3. 完整代码
1 | |
三、性能分析
堆高度为logN,因此在堆中插入元素和删除最大元素的复杂度都为logN
堆排序:要对N个节点进行下沉操作,所以时间复杂度为NlogN
堆排序为原地排序,不需要额外空间
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!