數據結構系列(6)之 徹底二叉堆

本文將主要講述在堆排序和優先級隊列中使用的一種數據結構,二叉堆;java

1、結構概述

徹底二叉堆,首先在邏輯上是樹形結構,徹底二字則代表是徹底的二叉樹,其結構如圖所示:api

heap

結構性: 正是由於是徹底結構的二叉樹,因此能夠將節點映射到數組中,其運算關係以下,i 表示數組下標:數組

  • 父節點:(i - 1) >> 1;
  • 左孩子:1 + (i << 1);
  • 右孩子:(1 + i) << 1;

堆序性: 在堆結構中,其任一父節點的優先級都高於其子節點,圖中的數字越小,表示優先級越高;數據結構

API: 對於堆結構而言,最重要的幾個接口:ui

insert() // 插入節點
getMax() // 獲取優先級最高的節點
delMax() // 刪除優先級最高的節點


2、插入節點

插入節點時候主要分兩步:3d

  • 首先將節點插入隊尾,對於數組而言其時間複雜度爲 O(1)
  • 而後與其父節點比較,若是新節點優先級更高,則與父節點交換,直至其優先級不大於父節點;(此過程稱爲上濾

其具體過程如圖所示:code

heapinsert

其代碼以下:blog

public void insert(E e) {
  if (size == data.length) throw new IllegalArgumentException("heap is full");
  data[size] = e;
  siftUp(size);
  size++;
}

private int siftUp(int i) {
  while (i > 0) { // 還有父節點
    int p = parent(i);
    if (cmp(data[i], data[p]) <= 0) break;
    swap(i, p);
    i = p;
  }
  return i;
}

private void swap(int i, int j) {
  Object t = data[i];
  data[i] = data[j];
  data[j] = t;
}


3、刪除、獲取節點

刪除首節點時候一樣分兩步:排序

  • 首先用隊尾的節點替換首節點;
  • 而後與兩個子節點比較,若是父節點優先級不是最高,則用子節點中優先級最高的節點替換,直至父節點的優先級最高;(此過程稱爲下濾

其具體過程如圖所示:接口

heapget

具體代碼以下:

public E delMax() {
  E e = (E) data[0];
  data[0] = data[--size];
  shiftDown(0);
  return e;
}

private int shiftDown(int i) {
  int j;
  while (i != (j = properParent(i))) {  // 若是父節點優先級不是最高
    swap(i, j);
    i = j;
  }
  return i;
}

private int properParent(int i) {
  int l = lc(i);
  if (l >= size) return i;
  int max = cmp(data[i], data[l]) >= 0 ? i : l;
  int r = rc(i);
  if (r >= size) return max;
  return cmp(data[max], data[r]) >= 0 ? max : r;
}


4、建堆

建堆的時候:

  • 首先構建二叉堆數組;
  • 而後最後一個父節點開始向上,一次執行下濾;

其具體過程如圖所示:

heapbuild

具體代碼以下:

public void build() {
  for (int i = parent(size - 1); i > -1 && i < size; i--)
    shiftDown(i);
}


5、堆排序

堆排序的整個過程,能夠將數組分紅兩個部分,徹底二叉堆部分和已排序部分,每次將堆的首節點和尾節點交換,同時已排序部分加一,而後二叉堆復位,一直重複指到堆爲空;

其具體過程以下:

heapbuild


heapbuild

其具體代碼以下:

public void heapSort(int hi) {
  // 建堆
  while (size > 0) data[--hi] = delMax();
}


總結

  • 對於徹底二叉堆而言,它本質的特徵是堆序性,只是當其構成徹底二叉樹的時候,能夠直接使用數組表示,其查詢的效率更高;
相關文章
相關標籤/搜索