目录
堆的概念以及问题思考
堆的概念:如果有关键字集合k = {k0,k1,k2,......,k(n-1)},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,且满足Ki <= K2i+1 且Ki <= K2i+2(或Ki >= K2i+1 且Ki >= K2i+2)i = 0,1,2.....,则称为小堆(或大堆)。将根节点最大的堆称为大根堆,将根节点最小的堆称为小根堆。
上图为大根堆举例.
若要创建一个大根堆并实现其插入删除操作,需要进行以下步骤:
①首先需要传入一个堆,以传递数组的方式,先初始化一个堆;
例如要传入的数据为:5 8 12 22 15 32 11,则首先初始化,初始化的结果如下图所示.
②然后对该堆进行调整,将其调整为大根堆,采取从根节点向下调整的方式来实现大根堆的创建.
③在建成大根堆后再对其进行插入删除操作,插入删除后依旧满足大根堆性质,具体实现思想在下午代码中描述.
初始化堆并将其调整为大根堆
初始化堆
在初始化时,需要定义一个数组,初始化数组大小为10,作为底层来实现堆,再定义usedSize用来记录堆中的元素个数,传入一个数组,将其抽象为堆结构,即完成初始化操作.
除此之外,还需写出一个判满和扩容的方法,若插入数据时,该堆底层的数组已满,则对其进行扩容,扩容的空间为当前空间的二倍.
public class TestHeap {
public int[] elem;
public int usedSize;
public TestHeap() {
this.elem = new int[10];
}
//扩容
public void dilatation() {
this.elem = Arrays.copyOf(this.elem, this.elem.length*2);
}
//判断数组是否满
public boolean isFull() {
return this.usedSize == this.elem.length;
}
//初始化堆
public void createHeap(int[] array) {
for(int i = 0; i < array.length; i++) {
if(isFull()) {
dilatation();
}
this.elem[i] = array[i];
this.usedSize++;
}
}
}
调整堆为大根堆
在将堆调整为大根堆的过程中采用向下调整的方式,首先利用usedSize找到要调整的第一个节点,具体如下图所示:
再判断elem[parent]和elem[child]哪个大,如果父亲结点大,则将两个结点的值互换,反之,则不需要做出改变.
在交换后,需要进行如下代码,意在对于交换后的结点判断其是否也满足大根堆,如果不满足,则进行调整,如果满足,则无需进行操作.
parent = child;
child = 2*parent+1;
具体代码实现:
/**
* 向下调整
* @param parent:每次调整的根节点
* @param len:每次结束的位置
*/
public void shiftDown(int parent, int len) {
int child = (2*parent) + 1;
while(child < len) {
//判断该节点是否有右孩子
if(child+1 < len && this.elem[child+1] > this.elem[child]) {
child ++;
}
//保证child下标一定为左右孩子最大值的下标
if(this.elem[child] > this.elem[parent]) {
int tmp = this.elem[parent];
this.elem[parent] = this.elem[child];
this.elem[child] = tmp;
parent = child;
child = 2*parent + 1;
}else {
break;
}
}
}
大根堆的插入和删除
大根堆的插入
之后利用uedSize找到第一个要比较的结点,具体语句为如下代码
int parent = (child-1) / 2;
之后一直向上调整,直至整个堆被调整为大根堆为止,具体代码实现如下:
/**
* 向上调整
* @param child 每次要调整的孩子结点
*/
public void shiftUp(int child) {
int parent = (child-1) / 2;
while(parent >= 0) {
if(this.elem[parent] < this.elem[child]) {
int tmp = this.elem[parent];
this.elem[parent] = this.elem[child];
this.elem[child] = tmp;
child = parent;
parent = (child-1) / 2;
} else {
break;
}
}
}
/**
* 大根堆的插入
* @param val 要插入大根堆的值
*/
public void push(int val) {
if(isFull()) {
dilatation();
}
this.elem[this.usedSize++] = val;
shiftUp(this.usedSize-1);
}
大根堆的删除
进行大根堆删除的主要思想为将下标为0位置的元素与数组最后一个元素互换,之后数组长度-1,再将目前的堆重新向下调整为大根堆即完成删除操作.
具体代码如下:
/**
* 判断大根堆是否为空
* @return 是否为空(true/false)
*/
public boolean isEmpty() {
return this.usedSize == 0;
}
/**
* 删除操作
* @return 删除的值
*/
public int pop() {
if(isEmpty()) {
throw new RuntimeException("堆为空,无法进行删除操作");
}
int tmp = this.elem[0];
this.elem[0] = this.elem[this.usedSize-1];
this.elem[this.usedSize-1] = tmp;
this.usedSize--;
shiftDown(0, this.usedSize);
return tmp;
}
完整代码
import java.util.Arrays;
public class TestHeap {
public int[] elem;
public int usedSize;
public TestHeap() {
this.elem = new int[10];
}
/**
* 扩容
*/
public void dilatation() {
this.elem = Arrays.copyOf(this.elem, this.elem.length*2);
}
/**
* @return 数组是否为满
*/
public boolean isFull() {
return this.usedSize == this.elem.length;
}
/**
* 向下调整
* @param parent:每次调整的根节点
* @param len:每次结束的位置
*/
public void shiftDown(int parent, int len) {
int child = (2*parent) + 1;
while(child < len) {
//判断该节点是否有右孩子
if(child+1 < len && this.elem[child+1] > this.elem[child]) {
child ++;
}
//保证child下标一定为左右孩子最大值的下标
if(this.elem[child] > this.elem[parent]) {
int tmp = this.elem[parent];
this.elem[parent] = this.elem[child];
this.elem[child] = tmp;
parent = child;
child = 2*parent + 1;
}else {
break;
}
}
}
/**
* 创建大根堆
* @param array 传入的数据
*/
public void createHeap(int[] array) {
for(int i = 0; i < array.length; i++) {
this.elem[i] = array[i];
this.usedSize++;
}
for(int i = (this.usedSize-1-1) / 2; i >= 0; i--) {
shiftDown(i, this.usedSize);
}
}
/**
* 向上调整
* @param child 每次要调整的孩子结点
*/
public void shiftUp(int child) {
int parent = (child-1) / 2;
while(parent >= 0) {
if(this.elem[parent] < this.elem[child]) {
int tmp = this.elem[parent];
this.elem[parent] = this.elem[child];
this.elem[child] = tmp;
child = parent;
parent = (child-1) / 2;
} else {
break;
}
}
}
/**
* 大根堆的插入
* @param val 要插入大根堆的值
*/
public void push(int val) {
if(isFull()) {
dilatation();
}
this.elem[this.usedSize++] = val;
shiftUp(this.usedSize-1);
}
/**
* 判断大根堆是否为空
* @return 是否为空(true/false)
*/
public boolean isEmpty() {
return this.usedSize == 0;
}
/**
* 删除操作
* @return 删除的值
*/
public int pop() {
if(isEmpty()) {
throw new RuntimeException("堆为空,无法进行删除操作");
}
int tmp = this.elem[0];
this.elem[0] = this.elem[this.usedSize-1];
this.elem[this.usedSize-1] = tmp;
this.usedSize--;
shiftDown(0, this.usedSize);
return tmp;
}
}