二叉堆和堆排序
二叉堆数据结构
二叉堆是一种特殊的二叉树,有以下两个特性:
- 它是一颗完全二叉树,表示树的每一层都有左侧和右侧子节点(除了最后一层的叶节点),并且最后一层的叶节点尽可能都是左侧子节点,这叫作结构特性。
- 二叉堆不是最小堆就是最大堆。最小堆允许你快速导出树的最小值,最大堆允许你快速导出树的最大值。所有的节点都大于等于(最大堆)或小于等于(最小堆)每个它的子节点。这叫作堆特性。
尽管二叉堆是二叉树,但并不一定是二叉搜索树。在二叉堆里,每个子节点都要大于等于父节点(最小堆)或小于等于父节点(最大堆)。
创建最小堆类
import { defaultCompare } from './util';export class MinHeap {constructor(compareFn = defaultCompare) {this.compareFn = compareFn;this.heap = [];}}
1.二叉树的数组表示。
二叉树有两种表示方式。第一种是使用一个动态的表示方式,也就是指针。第二种是使用一个数组,通过索引值检索父节点、左侧和右侧子节点的值。
要访问使用普通数组的二叉树节点,可以用下面的方式操作index。
对于给定位置index的节点:
它的左侧子节点的位置是2*index+1;
它的右侧子节点的位置是2*index+2;
它的父节点位置是index/2。
用上面的方法访问特定节点,我们可以把下面的方法加入MinHeap类。
getLeftIndex(index) {return 2 * index + 1;}getRightIndex(index) {return 2 * index + 2;}getParentIndex(index) {if(index === 0) {return undefined;}return Math.floor((index - 1) / 2);}
我们可以在堆数据结构中进行三个主要操作。
insert(value):向堆中插入一个新的值。成功返回true,否则返回false。
extract():移除最小值(最小堆)或最大值(最大堆)并返回这个值。
findMinimum():返回最小值或最大值且不会移除这个值。
//向堆中插入值//指将值插入堆的底部叶节点再执行siftUp方法,表示要将这个值和它的父节点进行交换//直到父节点小于这个插入的值。insert(value) {if(value != null) {this.heap.push(value);this.siftUp(this.heap.length - 1);return true;}return false;}//上移操作siftUp(index) {let parent = this.getParentIndex(index);while(index > 0 && this.compareFn(this.heap[parent], this.heap[index]) > this.compareFn.BIGGER_THAN) {swap(this.heap, parent, index);index = parent;parent = this.getParentIndex(index);}}//从堆中找到最小值或最大值//在最小堆中,最小值总是位于数组的第一个位置size() {return this.heap.length;}isEmpty() {return this.size() === 0;}findMinimum() {return this.isEmpty() ? undefined : this.heap[0];}// 导出堆中的最小值或最大值// 移除最小值或最大值表示移除数组中的第一个元素。在移除后,我们将堆的最后一个元素移动至根部并执行siftDown函数// 表示我们将交换元素直到堆的结构正常。extract() {if(this.isEmpty()) {return undefined;}if(this.size() === 1) {return this.heap.shift();}const removedValue = this.heap.shift();this.heap[0] = this.heap.pop()this.siftDown(0);return removedValue}siftDown(index) {let element = index;const left = this.getLeftIndex(index);const right = this.getRightIndex(index);const size = this.size();if(left < size && this.compareFn(this.heap[element], this.heap[left]) > Compare.BIGGER_THAN) {element = left;}if(right < size && this.compareFn(this.heap[element], this.heap[right]) > Compare.BIGGER_THAN) {element = right;}if(index !== element) {swap(this.heap, index, element);this.siftDown(element);}}function swap(array, a, b ) {const temp = array[a];array[a] = array[b];array[b] = temp;}
堆排序算法
- 用数组创建一个最大堆用作数据源。
- 在创建最大堆后,最大的值会被存储在堆的第一个位置。我们要将它替换为堆的最后一个值,将堆的大小减1.
- 最后我们将堆的根节点下移并重复步骤2直到堆的大小为1。
我们用最大堆得到一个升序排列的数组,如果想要数组降序排列,可以用最小堆代替。
function heapSort(array, compareFn = defaultCompare) {let heapSize = array.length;buildMaxHeap(array, compareFn);while(heapSize > 1) {swap(array, 0, --heapSize);heapify(array, 0, heapSize, compareFn);}return array;}function buildMaxHeap(array, compareFn) {for(let i = Math.floor(array.length / 2); i >= 0; i -= 1) {heapify(array, i, array.length, compareFn);}return array;}
