堆排序
问:
-
堆结构
-
堆排序
-
已知存在一个数组,它的每一个元素最多移动不超过K个位置就能到正确的位置。如何排序 解:
-
将一个数组按顺序看作完全二叉树,就是一个堆结构。根节点的关键字既大于或等于左子树的关键字值,又大于或等于右子树的关键字值,称为大根堆
-
将数组看作堆结构。先将这个长度为heapSize的数组转为大根堆,把根节点与最后一个节点交换位置,我们就将把堆的最大值放到了正确的位置,然后将heapSize--。现在将换到根节点的值与它的左右子节点(如果存在的话)中较大的比较,若根节点较小,就把大的子节点换上来并且递归这个操作,节点的下标不得超出heapSize。当递归结束,就会再次得到大根堆。
const arr = [3,21,31,235,435,43,3,4,12,]
// 转为大根堆结构
function heapInsert(idx) {
while (arr[idx] > arr[Math.floor((idx - 1) / 2)]) {
// 若当前值大于自己的父节点就和父节点交换位置
[arr[idx],arr[Math.floor((idx - 1) / 2)]] = [arr[Math.floor((idx - 1) / 2)],arr[idx]]
idx = Math.floor((idx - 1) / 2)
}
}
function heapfy(idx, heapSize) { // 找出子节点中较大的一个与当前节点比较,若小于子节点就交换位置
let targetIdx = idx
let leftNodeIdx = 2 * idx + 1
// 从三个节点中,选出最大节点
if (leftNodeIdx < heapSize && arr[leftNodeIdx] > arr[idx]) targetIdx = leftNodeIdx
if (leftNodeIdx + 1 < heapSize && arr[leftNodeIdx + 1] > arr[targetIdx]) targetIdx = leftNodeIdx + 1
// 交换位置
if (idx !== targetIdx) {
[arr[idx], arr[targetIdx]] = [arr[targetIdx], arr[idx]]
heapfy(targetIdx, heapSize)
}
}
function heapSort() {
let heapSize = arr.length -1
// 遍历数组,转为大根堆
for (let i =0; i< arr.length; i++) {
heapInsert(i);
}
// 将大根堆根节点与最后一位交换位置,然后将这个最大值移出堆结构
while (heapSize > 0) {
[arr[0], arr[heapSize]] = [arr[heapSize], arr[0]]
heapSize--
heapfy(0, heapSize)
}
}
heapSort()
- 把原数组从0开始截取K+1长度,转为小根堆,然后根节点就是最小值,将根节点放入结果数组,将原数组的下一位纳入堆解构转为小根堆。
const arr = [3,7,6,5,2,4,6,8,5]
const k = 5
function heapInsert(idx, arr) {
while (arr[idx] < arr[Math.floor((idx - 1) / 2)]) {
// 若当前值小于自己的父节点就和父节点交换位置
[arr[idx],arr[Math.floor((idx - 1) / 2)]] = [arr[Math.floor((idx - 1) / 2)],arr[idx]]
idx = Math.floor((idx - 1) / 2)
}
}
function heapSort (arr, k) {
// 先把数组的前k位截取出来
const heapArr = arr.splice(0, k)
// 结果数组
const res = []
// 遍历数组,转为小根堆
while (heapArr.length > 0) {
for (let i = 0; i < heapArr.length; i++) {
heapInsert(i, heapArr);
}
// 小根堆根节点放入结果数组
res.push(heapArr.shift())
// 将原数组的第一位再加入堆
arr.length && heapArr.push(arr.shift())
}
return res
}
heapSort(arr, k)