Куча — это полное бинарное дерево (каждый корень имеет максимальное количество доступных значений). Полное бинарное дерево — это бинарное дерево, в котором все уровни полностью заполнены, за исключением, возможно, самого нижнего уровня, который заполняется слева. Бинарное дерево состоит из 2**(h+1) -1 узлов (h — высота бинарного дерева).
Есть два типа кучи:
- Максимальная куча
- Минимальная куча
Максимальная куча:Максимальная куча — это полное двоичное дерево, в котором каждый узел (элемент) имеет значение, большее или равное его дочерним узлам (потомкам).
Минимальная куча. Минимальная куча — это полное двоичное дерево, в котором каждый узел (элемент) имеет значение, меньшее или равное его дочерним узлам (потомкам).
Представление в виде массива минимальной кучи: [1,3,2,4,6,5] и максимальной кучи: [6,5,3,4,2,1]
Для объяснения давайте использовать Max heap и array[6,5,3,4,2,1].
Есть несколько вещей, которые нам нужно понять, прежде чем мы приступим к кодированию.
- Как найти родителя элемента?
- Как найти левого дочернего элемента элемента?
- Как найти правильный дочерний элемент элемента?
- Является ли текущий узел конечным узлом (конечный узел – это узел, не имеющий дочерних элементов)?
Родитель элемента.Чтобы найти родителя элемента, используйте формулу. Math.floor((index-1)/2). давайте предположим, что нам нужно найти родителя элемента 4, который в настоящее время имеет индекс 4.
const parent = Math.floor((index-1)/2);// that is Math.floor(4-1)/2 = 1(index of parent), // which is 5. so 5 is the parent of 4.
Левый дочерний элемент элемента. Чтобы найти левого дочернего элемента элемента, используйте формулу (2*index)+1. Предположим, нам нужно найти левого дочернего элемента 5 с индексом 1.
const leftChild = (2*index)+1; // that is (2*1)+1 = 3(index of left child) // according to this 4 which is at index 3 is the left child of 5.
Правый дочерний элемент. Чтобы найти правый дочерний элемент элемента, используйте формулу (2*index)+2. Предположим, нам нужно найти правильный дочерний элемент 5 с индексом 1.
const rightChild = (2*index)+2; // that is (2*1)+2 = 4(index of right child) // according to this 2 which is at index 4 is the right child of 3.
Проверка того, является ли текущий узел листовым узлом:Чтобы проверить, является ли текущий индекс листовым узлом, нам нужно проверить, превышает ли текущий индекс половину длины массива и меньше длины массива. Это означает, что дочерних узлов нет.
//so we can check through const leftChild = index >= Math.floor(this.value.length / 2) && index <= this.value.length - 1
«В этом блоге мы также рассмотрим несколько концепций»
- Вставка
- Удаление
- Увеличить
- И, наконец, работа над сортировкой кучи
Вставка
Предположим, у нас есть представление массива max heap, равное [6,5,3,4,2,1], и мы хотим вставить новый элемент в кучу , тогда нам нужно поместить элемент в массив и проверить с его родителем, удовлетворяет ли условие для максимальной кучи или нет (в основном, если родитель больше или равен дочернему элементу или нет.). если текущий узел не удовлетворяет условию, нам нужно поменять их местами и проверить наличие нового узла.
Вставка следует подходу кучи вверх, поскольку мы проверяем от листа к корню.
Временная сложность вставки:Количество обменов, происходящих во время вставки, может быть максимальным до высоты дерева, т.е. (log n), поэтому время сложности в наихудшем случае вставки — O(log n).
Удаление
В нашем массиве [6,5,3,4,2,1], если вы хотите удалить элемент, это должен быть корень element, поэтому в нашем случае корень равен 6. Как только мы удалим 6, последний элемент в массиве (т.е. - 1) займет место корневого элемента. Как только это будет сделано, нам нужно настроить дерево и сравнить левый и дочерний узел с новым корневым элементом, если текущая куча удовлетворяет свойствам максимальной кучи или нет. если нет, то нам нужно поменять их местами.
Удаление следует подходу heapify Down, поскольку мы сравниваем от корня к листу.
Временная сложность удаления:количество обменов, происходящих во время удаления, может быть максимальным до высоты дерева, т.е. (log n), поэтому временная сложность в наихудшем случае вставки — O(log n).
Нагромождение
Heapify — это процесс создания структуры данных кучи из двоичного дерева, представленного с помощью массива. Как только мы удаляем или добавляем элемент в кучу, нам нужно выполнить heapifyUp (после добавления) и heapifyDown (после удаления).
HeapifyUp означает обработку элементов в куче от листа до корня и обработку того, находятся ли элементы в порядке и удовлетворяют ли они максимальной куче или нет, если нет, нам нужно их поменять местами.
HeapifyDown означает обработку элементов в куче от корня к листу и обработку того, находятся ли элементы в порядке и удовлетворяют ли они максимальной куче или нет, если нет, нам нужно их поменять местами.
Heapify имеет временную сложность O(n).
Куча сортировки
Сортировка кучей в основном представляет собой концепцию сначала создания кучи, а затем удаления кучи и построения отсортированного массива. По мере удаления мы получаем максимальный элемент из доступного массива. Итак, мы рассмотрим работу обоих этих процессов.
- Построить кучу.
- Удалить элемент из кучи.
Предположим, у нас есть исходный массив [10,20,15,30,40]
Давайте рассмотрим несколько важных функций, которые мы собираемся использовать для нашей сортировки в куче.
- Чтобы найти родителя элемента по индексу
// to fetch parent of the node // the formulae is (index - 1 /2) parent(index) { return Math.floor((index - 1) / 2); }
- Чтобы найти индекс левого узла
// fetch index of left node from index of parent node leftNode(index) { return 2 * index + 1; }
- Чтобы найти индекс правого узла
// fetch index of right node from index of parent node rightNode(index) { return 2 * index + 2; }
- Чтобы определить, является ли текущий узел конечным узлом или нет
//if the index is more than the half of length of array and index is less than length of array, means, //there is no child node isLeaf(index) { return ( index >= Math.floor(this.value.length / 2) && index <= this.value.length - 1 ); }
- поменять местами два значения
// swap functionality swap(index, target) { [this.value[index], this.value[target]] = [ this.value[target], this.value[index], ]; }
- Чтобы выполнить heapifyUp
// this will check each node and compare if the current node is at right path or not while comparing it with its parent // this is a leaf to root approach heapifyUp(index) { let currentNodeIndex = index; let parentNodeIndex = this.parent(currentNodeIndex); // we need to perform while loop until we reach the top node and value of child is more than parent node while ( parentNodeIndex >= 0 && currentNodeIndex > 0 && this.value[currentNodeIndex] > this.value[parentNodeIndex] ) { // current child is greater than parent so we need to swap them this.swap(currentNodeIndex, parentNodeIndex); // now we need to set the current parent as child and find the new parent for the child node currentNodeIndex = parentNodeIndex; parentNodeIndex = this.parent(parentNodeIndex); } }
- Чтобы выполнить HeapifyDown
// if we remove an element from the heap , we need to perform heapifyDown // so that we compare if the nodes are at right position // this is root to leaf approach, there we compare the childrens with the current node heapifyDown(index) { // we need to check if current node is not a leaf if (!this.isLeaf(0)) { // fetch left node let leftNode = this.leftNode(index); // fetch right node let rightNode = this.rightNode(index); let biggerNode = index; // we need to compare if the left and right node are bigger or not if (this.value[biggerNode] < this.value[leftNode]) { biggerNode = leftNode; } if (this.value[biggerNode] < this.value[rightNode]) { biggerNode = rightNode; } // checking if we have any mismatch, if yes swap if (biggerNode != index) { this.swap(index, biggerNode); this.heapifyDown(biggerNode); } } }
- Чтобы извлечь максимальный элемент из кучи
// method to fetch the top element from the heap extractMax() { if (!this.value.length) return; // fetch the largest value const largestValue = this.value[0]; // replace with last node this.value[0] = this.value.pop(); // once we remove the top element, we need to perform heapifyDown to take the elements to its right place in heap this.heapifyDown(0); return largestValue; }
- Создать кучу из предоставленного массива
// build an heap from the array buildHeap(heapNodes) { this.value = heapNodes; for (let i = Math.floor(this.value.length / 2); i >= 0; i--) { // we need to perform heapifyDown to put elements to its right place this.heapifyDown(i); } }
Давайте перейдем к работе с сортировкой кучи, объединив все доступные функции.
мы будем использовать классы javascript для построения этого алгоритма. Ниже приведена сортировка кучей в коде.
class HeapSort { constructor() { this.value = []; // store the nodes } // to fetch parent of the node // the formulae is (index -1 /2) parent(index) { return Math.floor((index - 1) / 2); } // fetch index of left node from index of parent node leftNode(index) { return 2 * index + 1; } // fetch index of right node from index of parent node rightNode(index) { return 2 * index + 2; } //if the index is more than the half of length of array and index is less than length of array, means, //there is no child node isLeaf(index) { return ( index >= Math.floor(this.value.length / 2) && index <= this.value.length - 1 ); } // swap functionality to swap nodes if conditions not meet swap(index, target) { [this.value[index], this.value[target]] = [ this.value[target], this.value[index], ]; } //Adding a node to the last of the array and then perform heapifyUp to put node at its right position addNode(node) { this.value.push(node); //heapify up to put the node at right point this.heapifyUp(this.value.length - 1); } // this will check each node and compare if the current node is at right path or not while comparing it with its parent // this is a leaf to root approach heapifyUp(index) { let currentNodeIndex = index; let parentNodeIndex = this.parent(currentNodeIndex); // we need to perform while loop until we reach the top node and value of child is more than parent node while ( parentNodeIndex >= 0 && currentNodeIndex > 0 && this.value[currentNodeIndex] > this.value[parentNodeIndex] ) { // current child is greater than parent so we need to swap them this.swap(currentNodeIndex, parentNodeIndex); // now we need to set the current parent as child and find the new parent for the child node currentNodeIndex = parentNodeIndex; parentNodeIndex = this.parent(parentNodeIndex); } } // if we remove an element from the heap , we need to perform heapifyDown // so that we compare if the nodes are at right position // this is root to leaf approach, there we compare the childrens with the current node heapifyDown(index) { // we need to check if current node is not a leaf if (!this.isLeaf(0)) { // fetch left node let leftNode = this.leftNode(index); // fetch right node let rightNode = this.rightNode(index); let biggerNode = index; // we need to compare if the left and right node are bigger or not if (this.value[biggerNode] < this.value[leftNode]) { biggerNode = leftNode; } if (this.value[biggerNode] < this.value[rightNode]) { biggerNode = rightNode; } // checking if we have any mismatch, if yes swap if (biggerNode != index) { this.swap(index, biggerNode); this.heapifyDown(biggerNode); } } } // method to fetch the top element from the heap extractMax() { if (!this.value.length) return; // fetch the largest value const largestValue = this.value[0]; // replace with last node this.value[0] = this.value.pop(); // once we remove the top element, we need to perform heapifyDown to take the elements to its right place in heap this.heapifyDown(0); return largestValue; } // build an heap from the array buildHeap(heapNodes) { this.value = heapNodes; for (let i = Math.floor(this.value.length / 2); i >= 0; i--) { // we need to perform heapifyDown to put elements to its right place this.heapifyDown(i); } } }
Вы можете протестировать работу, предоставив массив через функцию buildHeap.
const heap = new HeapSort(); heap.buildHeap([10,20,15,30,40]); console.log(heap.extractMax()); console.log(heap.extractMax()); console.log(heap.extractMax()); console.log(heap.extractMax()); console.log(heap.extractMax());
Ответ:
Вы можете протестировать, добавив узлы:
const heap = new HeapSort(); heap.addNode(10); heap.addNode(20); heap.addNode(15); heap.addNode(30); heap.addNode(40); console.log(heap.extractMax()); console.log(heap.extractMax()); console.log(heap.extractMax()); console.log(heap.extractMax()); console.log(heap.extractMax());
Ответ такой же, как и выше.
Я попытался прокомментировать каждую функциональную часть алгоритма в коде. если у вас все еще есть какие-либо проблемы с пониманием этого, оставьте комментарий, и я постараюсь ответить на ваши вопросы.
Следите за новостями. Если вы впервые здесь, загляните в наш блог Алгоритмы с использованием Javascript, в котором мы расскажем о нескольких наиболее часто используемых алгоритмах с использованием javascript.
В следующем блоге мы рассмотрим Поиск в ширину.