堆
(1)什么是二叉堆
二叉堆本质上是一种完全二叉树,二叉堆的经典表示方法是使用一个数组表示,其中:
- 根结点为数组的第一个元素A[0]。
- 其它结点中,第i个结点和数组索引元素对应的关系为:
A[(i – 1) / 2] | 返回第i个结点的父结点 |
---|---|
A[(2 * i) + 1] | 返回第i个结点的左儿子结点 |
A[(2 * i) + 2] | 返回第i个结点的右儿子结点 |
它分为两个类型:
-
最大堆
最大堆任何一个父节点的值,都大于等于它左右孩子节点的值。
-
最小堆
最小堆任何一个父节点的值,都小于等于它左右孩子节点的值。
假设父节点的下标是parent,那么它的左孩子下标就是 2*parent+1;它的右孩子下标就是 2*parent+2 。
(2)二叉堆的主要操作
-
堆的构建
大顶堆
//从第一个非叶子节点开始依次对数组中的元素进行下沉操作 //和孩子节点的最大值max比较 //大于max — 不需要在下沉 //小于max — 和max交换位置 - 继续和下一层孩子节点比较,直到队列末尾 function ajustMaxHeap(array, index, length) { for (let i = 2 * index + 1; i < length; i = 2 * i + 1) { if (i + 1 < length && array[i + 1] > array[i]) { i++; } if (array[index] >= [array[i]]) { break; } else { [array[index], array[i]] = [array[i], array[index]]; index = i; } } } function createMaxHeap(arr, length) { for (let i = Math.floor(length / 2) - 1; i >= 0; i--) { ajustMaxHeap(arr, i, length); } return arr; }
小顶堆
//从第一个非叶子节点开始依次对数组中的元素进行下沉操作 //和孩子节点的最小值min比较 //小于min — 不需要在下沉 //大于min — 和min交换位置(下沉) - 继续和下一层孩子节点比较,直到队列末尾 function ajustMinHeap(array, index, length) { for (let i = 2 * index + 1; i < length; i = 2 * i + 1) { if (i + 1 < length && array[i + 1] < array[i]) { i++; } if (array[index] < [array[i]]) { break; } else { [array[index], array[i]] = [array[i], array[index]]; index = i; } } } function createMinHeap(arr, length) { for (let i = Math.floor(length / 2) - 1; i >= 0; i--) { ajustMinHeap(arr, i, length); } return arr; }
-
堆的插入
//由于堆属于优先队列,只能从末尾添加 //添加后有可能破坏堆的结构,需要从下到上进行调整 //如果元素小于父元素,上浮 //以小顶堆为例: function minHeapAdd(array = [], element) { array.push(element); if (array.length > 1) { let index = array.length - 1; let target = Math.floor((index - 1) / 2); while (target >= 0) { array[target]); if (array[index] < array[target]) { [array[index], array[target]] = [array[target], array[index]] index = target; target = Math.floor((index - 1) / 2); } else { break; } } } return array; }
-
堆的移除
//由于堆属于优先队列,只能从头部移除 //移除头部后,使用末尾元素填充头部,开始头部下沉操作 //以小顶堆为例: function minHeapPop(array = []) { let result = null; if (array.length > 1) { result = array[0]; array[0] = array.pop(); ajustMinHeap(array, 0, array.length); } else if (array.length === 1) { return array.pop(); } return result; }
-
封装
class Heap { constructor(arr, type) { this.data = [...arr]; this.type = type; this.size = this.data.length; } create() { for (let i = Math.floor((this.size / 2) - 1); i >= 0; i--) { this.ajust(i); } } ajust (index) { const arr = this.data; for (let i = 2 * index + 1; i < this.size; i = 2 * i + 1) { if (i + 1 < this.size) { if ((this.type === 'max' && arr[i + 1] > arr[i]) || (this.type === 'min' && arr[i + 1] < arr[i])) { i++; } } if ((this.type === 'max' && arr[index] < [arr[i]]) || (this.type === 'min' && arr[index] > [arr[i]])) { [arr[index], arr[i]] = [arr[i], arr[index]]; index = i; } else { break; } } } add (element) { const arr = this.data; arr.push(element); if (this.size > 1) { let index = this.size - 1; let target = Math.floor((index - 1) / 2); while (target >= 0) { if ((this.type === 'min' && arr[index] < arr[target]) || (this.type === 'max' && arr[index] > arr[target])) { [arr[index], arr[target]] = [arr[target], arr[index]] index = target; target = Math.floor((index - 1) / 2); } else { break; } } } } pop () { const arr = this.data; let result = null; if (this.size > 1) { result = arr[0]; arr[0] = arr.pop(); this.ajust(0, this.size); } else if (this.size === 1) { return arr.pop(); } return result; } } var heap = new Heap('max'); heap.add(6) heap.add(10) console.log(heap.value); console.log(heap.pop()); console.log(heap.value);