每个父节点都小于他的左,右,孩子节点的值,最小堆
var count = 0;function up_adjust (arr) { // 下沉操作let childIndex = arr.length - 1;// 取余二等于零 就是一个右孩子let parentIndex = childIndex - 1;let temp = arr[childIndex];while (childIndex > 0 && temp < arr[parentIndex]) {count++;// 子元素存在 并且 子元素小于父元素// 父元素下沉arr[childIndex] = arr[parentIndex];childIndex = parentIndex;parentIndex = childIndex - 1;}// 子元素上浮arr[childIndex] = temp;}function down_adjust(parentIndex, len, arr) { // 上浮操作// parentIndex 待下沉目标// len 堆的长度范围// arr 二叉堆数组let temp = arr[parentIndex];let childIndex = 2 * parentIndex + 1; // 左孩子while (childIndex < len) {count++;if (childIndex + 1 < len && arr[childIndex + 1] < arr[childIndex]) {// 右孩子存在,并且右孩子小于左孩子childIndex += 1;}if (temp <= arr[childIndex]) {// 父节点小于任何一个孩子的值,直接跳出break;}// 父元素下沉,子元素上浮arr[parentIndex] = arr[childIndex];parentIndex = childIndex;childIndex = 2 * childIndex + 1; // 左孩子的下标}// 子元素上浮arr[parentIndex] = temp;}function build_heap(arr) {for (let i = arr.length - 2; i >= 0; i--) {down_adjust(i, arr.length, arr);}}var a = [7, 1, 3, 10, 5, 2, 8, 9, 6]build_heap(a); // 最小二叉堆的自均衡while (a.length) {console.log(a.shift()); // 输出0~9build_heap(a)}console.log(count); // 23
上面的代码是书本里面介绍的方法,简直扯淡,完全就是遍历,下面是二叉堆的上浮
var count = 0;function up_adjust (arr) { // 下沉操作let childIndex = arr.length - 1;let childOffset = childIndex % 2 === 0 ? 2 : 1; // 右孩子// 取余二等于零 就是一个右孩子let parentIndex = (childIndex - childOffset) / 2;let temp = arr[childIndex];while (childIndex > 0 && temp < arr[parentIndex]) {count++;// 子元素存在 并且 子元素小于父元素// 父元素下沉arr[childIndex] = arr[parentIndex];childIndex = parentIndex;childOffset = childIndex % 2 === 0 ? 2 : 1; // 右孩子// 取余二等于零 就是一个右孩子parentIndex = (childIndex - childOffset) / 2;}// 子元素上浮arr[childIndex] = temp;}var a = [1, 5, 2, 6, 7, 3, 8, 9, 10, 0]up_adjust(a);console.log(a); [0, 1, 2, 6, 5, 3, 8, 9, 10, 7]console.log(count); // 3
每个父节点都大于他左右孩子的值 最大堆
优先队列,每次把头元素出掉,然后树自均衡一下,然后在出,在自均衡。
代码略
为什么优先队列比循环排序输出更优呢,下面省略if比较的执行次数计算
假设长度为10的无序数值数值,要排序输出,最少要55次执行,冒泡排序执行45(10 * 10 / 2 + 10 / 2 - 10)次,循环输出10次。
优先队列,
出队列1,首次自均衡 9 - 2 = 7
出队列1 ,自均衡 8 - 2 = 6
…
10次输出完毕,7+6+5+4+3+2+1 = 28次 ,构建二叉堆1,1(和树深度一致) …
[1, 1,1,2,2,2,2,3,3,3] 最大 20次 总共 45次
如果100次的话
循环 99 99 / 2 + 99 / 2 = 4950
优先队列 97 97 /2 + 97 / 2 = 4753 + 127 = 4880 比循环更优先,但是按实际情况,可能次数更少一些
// 计算树的层次for (let i = 100, x = 1; i > 1; i /= 2, x++) {console.log(i, x);}/*100 1VM1643:2 50 2VM1643:2 25 3VM1643:2 12.5 4VM1643:2 6.25 5VM1643:2 3.125 6VM1643:2 1.5625 77层, 除掉根2一次方2二次方...2的六次方*/var count = 0for (let i = 6; i > 0; i--) {count += Math.pow(2, i)}126 + 1
