Java中的最大堆数据结构实现

时间:2020-02-23 14:34:35  来源:igfitidea点击:

最大堆是完整的二叉树,其中节点的值大于或者等于其子代的值。
Max Heap数据结构对于使用堆排序对数据进行排序非常有用。

在本教程中,我们将介绍从头开始在Java中实现最大堆所需的所有知识。

Java中最大堆数据结构的实现

我们使用数组表示堆。
由于堆是完整的二叉树,因此不会浪费空间。

例如,让我们考虑如下的堆:

数组表示为:

最大堆的声明如下:

static class MaxHeap {
      private int[] Heap; //array 
      private int size;
      private int maxsize; 

      public MaxHeap(int size) {
          this.maxsize = size;
          this.size = 0;
          Heap = new int[this.maxsize + 1];
          Heap[0] = Integer.MAX_VALUE;
      }

堆是存储最大堆的数组。
构造函数采用大小并将数组的第0个元素初始化为无穷大。
我们从索引1开始我们的堆。

1.获取节点的父节点

由于我们将堆存储为数组,因此获取节点的父节点变得更加容易。

对于位于位置i的元素,其父元素的位置由给出:

(i)/2

在实现过程中,我们可以使用以下方法获取父项:

private int parent(int pos) {
          return pos/2;
      }

2.为节点获取子代

对于位置i处的节点,其子级由以下公式给出:

左孩子:

(2*i)

合适的孩子:

(2*i)+ 1

注意:当堆从索引1开始时,这是正确的。
如果堆从位置0开始,则左子对象和右子对象的值分别为(2 * i)+1和(2 * i)+2。

在代码中,我们如下实现:

private int leftChild(int pos) {
          return (2 * pos) ;
      }

private int rightChild(int pos) {
          return (2 * pos) + 1;
      }

3.堆新插入的元素

将元素插入堆后,它可能不满足堆属性。
在这种情况下,我们需要调整堆的位置以使其再次成为堆。
此过程称为堆整。

要在最大堆中堆积一个元素,我们需要找到其最大子元素并将其与当前元素交换。
我们继续此过程,直到每个节点上的heap属性都满足为止。

为了堆积,我们从根部向下移动到叶子。
因此,这也称为Down Heapify。

另一个需要注意的有趣点是,我们仅在非叶节点上执行down heapify。

下堆函数的代码是:

private void downHeapify(int pos) {

//checking if the node is a leaf node 
          if (pos >= (size/2) && pos <= size)
              return;

//checking if a swap is needed
          if (Heap[pos] < Heap[leftChild(pos)] ||
                  Heap[pos] < Heap[rightChild(pos)]) {

//replacing parent with maximum of left and right child 
              if (Heap[leftChild(pos)] > Heap[rightChild(pos)]) {
                  swap(pos, leftChild(pos));

//after swaping, heapify is called on the children 
                  downHeapify(leftChild(pos));
              } else {

                 swap(pos, rightChild(pos));
//after swaping, heapify is called on the children 
                  downHeapify(rightChild(pos));
              }
          }
      }

交换函数如下:

private void swap(int fpos, int spos) {
          int tmp;
          tmp = Heap[fpos];
          Heap[fpos] = Heap[spos];
          Heap[spos] = tmp;
      }

您也可以使用while循环而不是递归来编写相同的代码。

在低调处理中,我们从父母转向孩子。
我们也可以采用自下而上的方式。
当我们以自下而上的方式移动时,我们会将节点与其父节点进行比较。
这称为Up Heapify。

大堆代码是:

private void heapifyUp(int pos) {
          int temp = Heap[pos];
          while(pos>0 && temp > Heap[parent(pos)]){
              Heap[pos] = Heap[parent(pos)];
              pos = parent(pos);
          }
          Heap[pos] = temp;
      }

我们使用while循环而不是递归编写了up heapify的代码。

4.插入新节点

将新元素添加到数组的末尾,并执行交换以确保堆属性成立。

插入算法为:

  • 增加堆大小
  • 将新元素保留在堆的末尾(数组)
  • 从下到上堆砌

插入的代码是:

public void insert(int element) {
          Heap[++size] = element; 
          int current = size;
          heapifyUp(current);
      }

5.删除/提取节点

要从堆中删除/提取节点,我们从根中删除元素。
根始终提供最大元素。

删除算法如下:

  • 将第一个元素复制到变量中。

  • 将最后一个元素复制到第一个位置(根)。

  • 调用downHeapify()。

删除的代码是:

public int extractMax() {
          int max = Heap[1];
          Heap[1] = Heap[size--];
          downHeapify(1);
          return max;
      }

其中我们使用size–减小堆的大小。

Java中Max Heap的完整实现

Max Heap的完整java实现如下。

package com.theitroad;

public class Main {
  static class MaxHeap {
      private int[] Heap;
      private int size;
      private int maxsize;

      public MaxHeap(int size) {
          this.maxsize = size;
          this.size = 0;
          Heap = new int[this.maxsize + 1];
          Heap[0] = Integer.MAX_VALUE;
      }

      private int parent(int pos) {
          return pos/2;
      }

      private int leftChild(int pos) {
          return (2 * pos)  ;
      }

      private int rightChild(int pos) {
          return (2 * pos) + 1;
      }

      private void swap(int fpos, int spos) {
          int tmp;
          tmp = Heap[fpos];
          Heap[fpos] = Heap[spos];
          Heap[spos] = tmp;
      }

      private void downHeapify(int pos) {
          if (pos >= (size/2) && pos <= size)
              return;

          if (Heap[pos] < Heap[leftChild(pos)] ||
                  Heap[pos] < Heap[rightChild(pos)]) {

              if (Heap[leftChild(pos)] > Heap[rightChild(pos)]) {
                  swap(pos, leftChild(pos));
                  downHeapify(leftChild(pos));
              } else {
                  swap(pos, rightChild(pos));
                  downHeapify(rightChild(pos));
              }
          }
      }
      private void heapifyUp(int pos) {
          int temp = Heap[pos];
          while(pos>0 && temp > Heap[parent(pos)]){
              Heap[pos] = Heap[parent(pos)];
              pos = parent(pos);
          }
          Heap[pos] = temp;
      }

      public void insert(int element) {
          Heap[++size] = element;

          int current = size;
          heapifyUp(current);

      }

      public void print() {
          for (int i = 1; i <= size/2; i++) {
              System.out.print(+ Heap[i] + ": L- " +
                      Heap[2 * i] + " R- " + Heap[2 * i + 1]);
              System.out.println();
          }
      }

      public int extractMax() {
          int max = Heap[1];
          Heap[1] = Heap[size--];
          downHeapify(1);
          return max;
      }
  }
  public static void main(String[] arg)
  {
    
      MaxHeap maxHeap = new MaxHeap(15);
      maxHeap.insert(1);
      maxHeap.insert(4);
      maxHeap.insert(2);
      maxHeap.insert(5);
      maxHeap.insert(13);
      maxHeap.insert(6);
      maxHeap.insert(17);

      maxHeap.print();
      System.out.println("The max is " + maxHeap.extractMax());
  }

} 

Output : 

17: L- 5 R- 13
5: L- 1 R- 4
13: L- 2 R- 6
The max is 17