堆
Heap
import Comparator from '../utils/Comparator.js';
/**
* Parent class for Min and Max Heaps.
*/
export default class Heap {
constructor(comparatorFunction) {
if (new.target === Heap) {
throw new TypeError('不可直接 construct Heap instance.)');
}
this.heapContainer = [];
this.compare = new Comparator(comparatorFunction);
}
getLeftChildIndex(parentIndex) {
return (2 * parentIndex) + 1;
}
getRightChildIndex(parentIndex) {
return (2 * parentIndex) + 2;
}
getParentIndex(childIndex) {
return Math.floor((childIndex - 1) / 2);
}
hasParent(childIndex) {
return this.getParentIndex(childIndex) >= 0;
}
hasLeftChild(parentIndex) {
return this.getLeftChildIndex(parentIndex) < this.heapContainer.length;
}
hasRightChild(parentIndex) {
return this.getRightChildIndex(parentIndex) < this.heapContainer.length;
}
parent(childIndex) {
return this.heapContainer[this.getParentIndex(childIndex)];
}
leftChild(parentIndex) {
return this.heapContainer[this.getLeftChildIndex(parentIndex)];
}
rightChild(parentIndex) {
return this.heapContainer[this.getRightChildIndex(parentIndex)];
}
/**
* @return {*}
*/
peek() {
if (this.heapContainer.length === 0) {
return null;
}
return this.heapContainer[0];
}
/**
* 移除堆顶一个元素
* @return {*}
*/
poll() {
if (this.heapContainer.length === 0) {
return null;
}
if (this.heapContainer.length === 1) {
return this.heapContainer.pop();
}
const item = this.heapContainer[0];
this.heapContainer[0] = this.heapContainer.pop();
this.heapifyDown();
return item;
}
/**
* 添加一个元素
* @return {*}
*/
add(item) {
this.heapContainer.push(item);
this.heapifyUp();
return this;
}
/**
* @param {number} customStartIndex
*/
heapifyUp(customStartIndex) {
let currentIndex = customStartIndex || this.heapContainer.length - 1;
while(
this.hasParent(currentIndex) &&
!this.pairIsInCorrectOrder(this.parent(currentIndex), this.heapContainer[currentIndex])
) {
this.swap(currentIndex, this.getParentIndex(currentIndex));
currentIndex = this.getParentIndex(currentIndex);
}
}
heapifyDown(customStartIndex = 0) {
let currentIndex = customStartIndex;
let nextIndex = null;
while(this.hasLeftChild(currentIndex)) {
if (
this.hasRightChild(currentIndex)
&& this.pairIsInCorrectOrder(this.rightChild(currentIndex), this.leftChild(currentIndex))
) {
nextIndex = this.getRightChildIndex(currentIndex);
} else {
nextIndex = this.getLeftChildIndex(currentIndex);
}
if (this.pairIsInCorrectOrder(
this.heapContainer[currentIndex],
this.heapContainer[nextIndex],
)) {
break;
}
this.swap(currentIndex, nextIndex);
currentIndex = nextIndex;
}
return this;
}
swap(indexOne, indexTwo) {
const tmp = this.heapContainer[indexTwo];
this.heapContainer[indexTwo] = this.heapContainer[indexOne];
this.heapContainer[indexOne] = tmp;
}
find(item, comparator = this.compare) {
const foundItemIndices = [];
for(let i = 0; i < this.heapContainer.length; i++) {
if (comparator.equal(item, this.heapContainer[i])) {
foundItemIndices.push(i);
}
}
return foundItemIndices;
}
remove(item, comparator = this.compare) {
// Find number of items to remove.
const numberOfItemsToRemove = this.find(item, comparator).length;
for (let iteration = 0; iteration < numberOfItemsToRemove; iteration += 1) {
const indexToRemove = this.find(item, comparator).pop();
if (indexToRemove === (this.heapContainer.length - 1)) {
this.heapContainer.pop();
} else {
this.heapContainer[indexToRemove] = this.heapContainer.pop();
const parentItem = this.parent(indexToRemove);
// 若 无parent 或 与parent顺序正确;heapify down
// 否则,heapify up.
if (
this.hasLeftChild(indexToRemove)
&& (
!parentItem ||
this.pairIsInCorrectOrder(parentItem, this.heapContainer[indexToRemove])
)
) {
this.heapifyDown(indexToRemove);
} else {
this.heapifyUp(indexToRemove);
}
}
}
return this;
}
/* istanbul ignore next */
pairIsInCorrectOrder(firstElement, secondElement) {
throw new Error(`
务必先实现 heap pair 比较方法
for ${firstElement} and ${secondElement} values.
`);
}
}
MaxHeap
import Heap from './Heap.js';
export default class MaxHeap extends Heap {
pairIsInCorrectOrder(firstElement, secondElement) {
// return firstElement >= secondElement ? true : false;
return this.compare.greaterThanOrEqual(firstElement, secondElement);
}
}
MinHeap
import Heap from './Heap.js';
export default class MinHeap extends Heap {
pairIsInCorrectOrder(firstElement, secondElement) {
// return firstElement >= secondElement ? true : false;
return this.compare.lessThanOrEqual(firstElement, secondElement);
}
}