堆是一个完全二叉树,分为大顶堆和小顶堆
class MaxHeap {
constructor() {
// 初始化时将位置 0 留空,方便计算子节点和父节点的索引关系
this.heap = [null];
}
insert(value) {
this.heap.push(value);
let currentIndex = this.heap.length - 1;
while (currentIndex > 1 && this.heap[Math.floor(currentIndex / 2)] < this.heap[currentIndex]) {
[this.heap[currentIndex], this.heap[Math.floor(currentIndex / 2)]] = [this.heap[Math.floor(currentIndex / 2)], this.heap[currentIndex]];
currentIndex = Math.floor(currentIndex / 2);
}
}
}
方式二:
let arr = [];
function heapInsert(num) {
arr.push(num);
let insertIndex = arr.length - 1;
while (num > arr[Math.floor((insertIndex - 1) / 2)]) {
//和(i-1)/2父节点交换位置
[arr[insertIndex], arr[Math.floor((insertIndex - 1) / 2)]] = [
arr[Math.floor((insertIndex - 1) / 2)],
arr[insertIndex],
];
insertIndex = Math.floor((insertIndex - 1) / 2);
}
}
heapInsert(6);
heapInsert(5);
heapInsert(4);
heapInsert(1);
heapInsert(3);
heapInsert(2);
heapInsert(8);
console.log(arr) //8 5 6 1 3 2 4
//原地建堆
function buildHeap(items, heapSize) {
while(heapSize < items.length-1) {
heapSize ++
heapify(items, heapSize)
}
}
function heapify(items, i) {
// 自下而上式堆化
当前元素索引取 Math.ceil(i/2)-1是因为,1和2的父元素索引都是0;
while (i/2 > 0 && items[i] <items[Math.ceil(i/2)-1]) {
swap(items, i, Math.ceil(i/2)-1); // 交换
i = Math.ceil(i/2)-1;
}
}
function swap(items, i, j) {
let temp = items[i]
items[i] = items[j]
items[j] = temp
}
// 测试
var items2 = [5,2,3,4,1]
buildHeap(items2, 0)
console.log(items2) // 1 2 3 5 4
// 原地建堆
// items: 原始序列
function buildHeap(items) {
let heapSize = items.length
// 从最后一个非叶子节点开始,然后遍历到序号为0的节点为止,使得完全二叉树能从左右两个子树的最底层开始往上整合,直到根节点
// 因为最后一排的节点不需要堆化
for (let i = Math.floor(heapSize/2); i >= 1; --i) {
heapify(items, heapSize, i);
}
}
//可以根据最后面的heapify,根据索引进行节点比较优化
function heapify(items, heapSize, i) {
// 当前节点比较交换后,还要继续比较交换后节点和其子左右节点大小,以保证当前节点的子树都满足大顶堆或小顶堆的结构
while (true) {
var maxIndex = i;
if(2*i <= heapSize && items[i] > items[i*2] ) {
maxIndex = i*2;
}
if(2*i+1 <= heapSize && items[maxIndex] > items[i*2+1] ) {
maxIndex = i*2+1;
}
if (maxIndex === i) break;
swap(items, i, maxIndex); // 交换
i = maxIndex;
}
}
function swap(items, i, j) {
let temp = items[i]
items[i] = items[j]
items[j] = temp
}
// 测试
var items = [,5, 2, 3, 4, 1]
buildHeap(items, items.length - 1)
console.log(items)
// [empty, 1, 2, 3, 4, 5]
class MaxHeap {
constructor() {
// 初始化时将位置 0 留空,方便计算子节点和父节点的索引关系
this.heap = [null];
}
insert(value) {
this.heap.push(value);
let currentIndex = this.heap.length - 1;
while (currentIndex > 1 && this.heap[Math.floor(currentIndex / 2)] < this.heap[currentIndex]) {
[this.heap[currentIndex], this.heap[Math.floor(currentIndex / 2)]] = [this.heap[Math.floor(currentIndex / 2)], this.heap[currentIndex]];
currentIndex = Math.floor(currentIndex / 2);
}
}
remove() {
if (this.heap.length === 1) return null;
let maxValue = this.heap[1];
this.heap[1] = this.heap.pop();
let currentIndex = 1;
while (true) {
let leftChildIndex = currentIndex * 2;
let rightChildIndex = currentIndex * 2 + 1;
let swapIndex = null;
if (leftChildIndex < this.heap.length && this.heap[leftChildIndex] > this.heap[currentIndex]) {
swapIndex = leftChildIndex;
}
if (
rightChildIndex < this.heap.length &&
this.heap[rightChildIndex] > this.heap[currentIndex] &&
this.heap[rightChildIndex] > this.heap[leftChildIndex]
) {
swapIndex = rightChildIndex;
}
if (!swapIndex) break;
[this.heap[currentIndex], this.heap[swapIndex]] = [this.heap[swapIndex], this.heap[currentIndex]];
currentIndex = swapIndex;
}
return maxValue;
}
}
//大顶堆删除提取第一个节点
//这里实现的是能在任何一个位置开始堆化(前提是除了待改动的节点,已经堆化了)
function heapify(arr, index) {
let heapSize = arr.length;
//左子节点
let left = index * 2 + 1;
//当还存在子节点时,left可以理解为子节点的第一个
while (left < heapSize) {
//获取两个子节点最大的那个索引,left + 1 < heapSize表示如果越界了,表面left是最后一个子节点,取left
let largest =
left + 1 < heapSize && arr[left] < arr[left + 1] ? left + 1 : left;
//待比较节点和子节点最大值比较
largest = arr[index] >= arr[largest] ? index : largest;
//子节点为最大值,退出
if (largest == index) {
break;
}
//交换最大值,最大值往上
[arr[index], arr[largest]] = [arr[largest], arr[index]];
index = largest;
left = index * 2 + 1;
}
}
let arr = [8, 5, 6, 1, 3, 2, 4];
let max = arr[0];
arr[0] = arr.pop();
heapify(arr, 0);
console.log(arr); //[ 6, 5, 4, 1, 3, 2 ]
class MaxHeap {
constructor() {
// 初始化时将位置 0 留空,方便计算子节点和父节点的索引关系
this.heap = [null];
}
insert(value) {
this.heap.push(value);
let currentIndex = this.heap.length - 1;
while (currentIndex > 1 && this.heap[Math.floor(currentIndex / 2)] < this.heap[currentIndex]) {
[this.heap[currentIndex], this.heap[Math.floor(currentIndex / 2)]] = [this.heap[Math.floor(currentIndex / 2)], this.heap[currentIndex]];
currentIndex = Math.floor(currentIndex / 2);
}
}
remove() {
if (this.heap.length === 1) return null;
let maxValue = this.heap[1];
this.heap[1] = this.heap.pop();
let currentIndex = 1;
while (true) {
let leftChildIndex = currentIndex * 2;
let rightChildIndex = currentIndex * 2 + 1;
let swapIndex = null;
if (leftChildIndex < this.heap.length && this.heap[leftChildIndex] > this.heap[currentIndex]) {
swapIndex = leftChildIndex;
}
if (
rightChildIndex < this.heap.length &&
this.heap[rightChildIndex] > this.heap[currentIndex] &&
this.heap[rightChildIndex] > this.heap[leftChildIndex]
) {
swapIndex = rightChildIndex;
}
if (!swapIndex) break;
[this.heap[currentIndex], this.heap[swapIndex]] = [this.heap[swapIndex], this.heap[currentIndex]];
currentIndex = swapIndex;
}
return maxValue;
}
}
// 堆排序
function heapSort(arr) {
const maxHeap = new MaxHeap();
for (const value of arr) {
maxHeap.insert(value);
}
for (let i = arr.length - 1; i >= 0; i--) {
arr[i] = maxHeap.remove();
}
}
const arr = [4, 2, 6, 3, 7, 9, 0];
heapSort(arr);
console.log(arr); // 输出:[0, 2, 3, 4, 6, 7, 9]
let arr = [8, 5, 6, 1, 3, 2, 4];
//开辟新数组
function heapSort(arr) {
let order = [];
while (arr.length) {
order.push(arr[0]);
if (arr.length == 1) {
break;
}
arr[0] = arr.pop();
heapify(arr, 0);
}
return order;
}
console.log(heapSort(arr)); //[8, 6, 5, 4, 3, 2, 1];
//原数组,空间复杂度为O(1)
function heapSort(arr,heapSize) {
[arr[0],arr[heapSize-1]]=[arr[heapSize-1],arr[0]];
heapSize--;
while(heapSzie>0){
//这里的heapify应该根据heapSize的长度建堆,上面的heapSize未实现
heapify(arr, 0, heapSize);
[arr[0],arr[heapSize-1]]=[arr[heapSize-1],arr[0]];
heapSize--;
}
}
heapSort(arr,arr.length);
console.log(heapSort(arr)); //[8, 6, 5, 4, 3, 2, 1];