数据结构系列(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();
}


总结

  • 对于彻底二叉堆而言,它本质的特征是堆序性,只是当其构成彻底二叉树的时候,能够直接使用数组表示,其查询的效率更高;
相关文章
相关标签/搜索