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