# JS data structure and algorithm heap

Keywords: less

### concept

The bottom layer of the heap is actually a complete binary tree, which can be implemented by arrays.

A binary tree that satisfies the following conditions:

1. Any node is larger or smaller than all its children (large root heap, small root heap)
2. It is always a complete tree, that is, the nodes of other layers are filled with elements except the lowest layer

The heap with the largest root node is called the maximum heap or the large root heap, and the heap with the smallest root node is called the minimum heap or the small root heap.

Leave the first element of the array empty for easy calculation. In this way, we can start from subscript 1, and the subscript variable is i, then:

• The left child node position is 2*i
• The position of the right child node is 2*i+1
• Parent node location is Math.floor(i/2)

The main operations related to the reactor (taking the large top reactor as an example) are as follows:

1. Adjust the end node of the heap so that the child node is always smaller than the parent node;
2. Create a build Max heap, and adjust all the data in the heap to make it a big heap;
3. Heap sort, remove the root node at the top of the heap, and do the iteration of big top heap adjustment.

### Large top reactor adjustment

``````/**
* Check and maintain the large top reactor from index
* @param arr  Array to be checked
* @param i    Start subscript of check
* @param size Heap size
*/
function maxHeapify(arr, i, size) {
let left = 2 * i
let right = left + 1

//The older of the left and right children
let maxlr = -1

//No left and right child nodes
if (left > size && right > size) {
return
}
//Only left child node
if (left <= size && right > size) {
maxlr = left
}
//Only the right child node
if (right <= size && left > size) {
maxlr = right
}
//There are left and right child nodes at the same time
if (left <= size && right <= size) {
maxlr = arr[left] < arr[right] ? right : left
}

if (arr[i] < arr[maxlr]) {
swap(arr, i, maxlr)
maxHeapify(arr, maxlr, size)
}
}

function swap(arr, i, j) {
let temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
Copy code``````

Non recursive writing:

``````/**
* Check and maintain the large top reactor from i
* @param arr Array to be arranged
* @param i Start subscript of check
* @param size Heap size
*/
function maxHeapify2(arr, i, size) {
let left, right, maxlr = -1

while (i < size) {
left = 2 * i
right = left + 1

//No left and right child nodes
if (left > size && right > size) {
break
}
//Only left child node
if (left <= size && right > size) {
maxlr = left
}
//Only the right child node
if (right <= size && left > size) {
maxlr = right
}
//There are left and right child nodes at the same time
if (left <= size && right <= size) {
maxlr = arr[left] < arr[right] ? right : left
}

if (arr[i] < arr[maxlr]) {
swap(arr, maxlr, i)
i = maxlr
}
}
}

function swap(arr, i, j) {
let temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
Copy code``````

### Create a large top heap

The purpose of creating a build Max heap is to transform an array into a large heap, and call Max heap from bottom to top to transform the array.

``````function buildMaxHeap(arr) {
if (!Array.isArray(arr)) return []

//Insert null in the first position of the array
arr.unshift(null)
let lastParentIndex = Math.floor((arr.length - 1) / 2)
for (let i = lastParentIndex; i > 0; i--) {
maxHeapify(arr, i, arr.length - 1)
}
arr.shift()
}
Copy code``````

### Heap sort

Heap-Sort is the interface algorithm of heap sorting. It needs to call Build-Max-Heap to transform the array into a large top heap. Then, it enters the iteration. In the iteration, the elements at the top and bottom of the heap are exchanged, and the length of the heap is shortened. Then, it calls Max-Heapify again to keep the nature of the large top heap.

Because the top element of the heap must be the largest element in the heap, after each operation, the largest element in the heap will be separated from the heap, repeated n-1 times, and the array sorting is completed.

``````function heapSort(arr) {
arr[0] !== null && arr.unshift(null)
for (let j = arr.length - 1; j > 0; j--) {
//Exchange the top and bottom elements to separate the largest elements
swap(arr, 1, j)
//Readjust the large top reactor
maxHeapify(arr, 1, j - 1)
}
arr.shift()
}
Copy code``````

Let's test the construction and sorting of the large top heap:

``````var arr = [5, 2, 8, 3, 1, 6, 9]
buildMaxHeap(arr)
console.log(arr)
heapSort(arr)
console.log(arr)

//[9,3,8,2,1,6,5]
//[1,2,3,5,6,8,9]
Copy code``````

### Complexity

Heap sorting is a kind of selective sorting, which consists of building initial heap + exchanging top and end elements of heap and rebuilding heap.

The derivation complexity of the initial heap is O(n). In the process of exchanging and rebuilding the heap, it needs to exchange n-1 times. In the process of rebuilding the heap, according to the properties of the complete binary tree, [log2(n-1),log2(n-2)...1] gradually decreases, which is approximately nlogn.

So the time complexity of heap sorting is generally considered as O(nlogn) level.

### Add element

Add the element to the end of the array and make a large heap adjustment

``````function maxHeapPush(arr = [], elem) {
arr.push(elem)
arr[0] !== null && arr.unshift(null)
let lastParentIndex = Math.floor((arr.length - 1) / 2)
for (let i = lastParentIndex; i > 0; i--) {
maxHeapify(arr, i, arr.length - 1)
}
arr.shift()
}
Copy code``````

### Pop up element

Each time we can only pop up the maximum value, that is, the root node. If we delete the root element directly, the whole heap will be destroyed. So we think about using an internal element to replace the position of the root node first. This element is obviously the last element, because the movement of the last element will not change the structure of the tree. After the exchange, the large top reactor was adjusted.

``````function maxHeapPop(arr = []) {
arr[0] !== null && arr.unshift(null)
swap(arr, 1, arr.length - 1)
const top = arr.pop()

let lastParentIndex = Math.floor((arr.length - 1) / 2)
for (let i = lastParentIndex; i > 0; i--) {
maxHeapify(arr, i, arr.length - 1)
}
arr.shift()
return top
}
Copy code``````

Test it:

``````var arr = [5, 2, 8, 3, 1, 6, 9]
buildMaxHeap(arr)
console.log(arr)
maxHeapPush(arr, 10)
console.log(arr)
maxHeapPop(arr)
console.log(arr)

//[10, 9, 8, 3, 1, 6, 5, 2]
//[9, 3, 8, 2, 1, 6, 5]
Copy code``````

The complete code is as follows:

``````function Heap(type = 'max') {
this.type = type
this.arr = []
}

Heap.prototype.build = function() {
this.arr.unshift(null)
let lastParentIndex = Math.floor((this.arr.length - 1) / 2)
for (let i = lastParentIndex; i > 0; i--) {
this.heapify(i, this.arr.length - 1)
}
this.arr.shift()
}

Heap.prototype.heapify = function(i, size) {
let left = 2 * i
let right = left + 1

//The older or younger of the left and right children
let lr = -1

//No left and right child nodes
if (left > size && right > size) {
return
}
//Only left child node
if (left <= size && right > size) {
lr = left
}
//Only the right child node
if (right <= size && left > size) {
lr = right
}
//There are left and right child nodes at the same time
if (left <= size && right <= size) {
lr = this.type === 'max' ? (this.arr[left] < this.arr[right] ? right : left) : (this.arr[left] > this.arr[right] ? right : left)
}

if ((this.type === 'max' && this.arr[i] < this.arr[lr]) || (this.type === 'min' && this.arr[i] > this.arr[lr])) {
this.swap(i, lr)
this.heapify(lr, size)
}
}

Heap.prototype.sort = function() {
this.arr[0] !== null && this.arr.unshift(null)
for (let j = this.arr.length - 1; j > 0; j--) {
this.swap(1, j)
this.heapify(1, j - 1)
}
this.arr.shift()
}

Heap.prototype.add = function(elem) {
this.arr.push(elem)
this.arr[0] !== null && this.arr.unshift(null)
let lastParentIndex = Math.floor((this.arr.length - 1) / 2)
for (let i = lastParentIndex; i > 0; i--) {
this.heapify(i, this.arr.length - 1)
}
this.arr.shift()
}

Heap.prototype.pop = function() {
this.arr[0] !== null && this.arr.unshift(null)
this.swap(1, this.arr.length - 1)
const top = this.arr.pop()

let lastParentIndex = Math.floor((this.arr.length - 1) / 2)
for (let i = lastParentIndex; i > 0; i--) {
this.heapify(i, this.arr.length - 1)
}
this.arr.shift()
return top
}

Heap.prototype.swap = function(i, j) {
let temp = this.arr[i]
this.arr[i] = this.arr[j]
this.arr[j] = temp
}

var heap = new Heap()
heap.add(5)
heap.add(2)
heap.add(8)
heap.add(3)
heap.add(1)
heap.add(6)
heap.add(9)
console.log(heap.arr)

//heap.build()
//console.log(heap.arr)

heap.add(10)
console.log(heap.arr)

heap.pop()
console.log(heap.arr)

heap.sort()
console.log(heap.arr)
Copy code``````

### Median in data flow

How to get the median in a data stream? If an odd number is read from the data stream, the median is the number in the middle after all the numbers are sorted.

If even numbers are read from the data stream, the median is the average of the middle two numbers after all the numbers are sorted. We use the Insert() method to read the data stream and the getmedia () method to get the median of the currently read data.

Step:

1. Maintain a large top heap and a small top heap. Total data:
• The values in the small top pile are all larger than those in the large top pile;
• The difference between 2 stacks is less than or equal to 1
1. When the total number of data after inserting a number is odd: make the number of small top stacks 1 more than that of large top stacks; when the total number of data after inserting a number is even, make the number of large top stacks the same as that of small top stacks.
2. When the total number of numbers is odd, the median is the small top pile head; when the total number of numbers is even, the median is the average of the two top piles.
``````const maxHeap = new Heap('max');
const minHeap = new Heap('min');
let count = 0;
function Insert(num) {
count++;
if (count % 2 === 1) {
maxHeap.add(num);
minHeap.add(maxHeap.pop());
} else {
minHeap.add(num);
maxHeap.add(minHeap.pop());
}
}
function GetMedian() {
if (count % 2 === 1) {
return minHeap.value[0];
} else {
return (minHeap.value[0] + maxHeap.value[0]) / 2
}
}
Copy code``````

### Minimum number of k

Enter n integers to find the smallest number of K. For example, if you enter 8 numbers of 4,5,1,6,2,7,3,8, the minimum 4 numbers are 1,2,3,4.

Step:

1. Build a large top heap with the first k numbers
2. Starting from the k-th number, compare with the maximum value of the large top heap. If it is smaller than the maximum value, exchange the positions of the two numbers and rebuild the large top heap
3. After one traverse, the number in the big top heap is the smallest k number in the whole data
``````function getLeastNumbers(arr, k) {
if (k > arr.length) {
return []
}
arr.unshift(null)
buildMaxHeap(arr, k + 1)

for (let i = k + 1; i < arr.length; i++) {
if (arr[i] < arr[1]) {
[arr[i], arr[1]] = [arr[1], arr[i]]

let lastParentIndex = Math.floor(k / 2)
for (let j = lastParentIndex; j > 0; j--) {
maxHeapify(arr, j, k)
}
}
}
return arr.slice(1, k + 1)
}

function buildMaxHeap(arr, size) {
let lastParentIndex = Math.floor(size / 2)
for (let i = lastParentIndex; i > 0; i--) {
maxHeapify(arr, i, size)
}
}

function maxHeapify(arr, i, size) {
let left = 2 * i
let right = left + 1

//The older of the left and right children
let maxlr = -1

//No left and right child nodes
if (left > size && right > size) {
return
}
//Only left child node
if (left <= size && right > size) {
maxlr = left
}
//Only the right child node
if (right <= size && left > size) {
maxlr = right
}
//There are left and right child nodes at the same time
if (left <= size && right <= size) {
maxlr = arr[left] < arr[right] ? right : left
}

if (arr[i] < arr[maxlr]) {
[arr[i], arr[maxlr]] = [arr[maxlr], arr[i]]
maxHeapify(arr, maxlr, size)
}
}

var arr = [4, 5, 1, 6, 2, 7, 3, 8]
var result = getLeastNumbers(arr, 4)
console.log(result)
//[4,3,1,2]
Copy code``````

Posted by tweek on Sat, 30 May 2020 17:16:31 -0700