堆和优先级队列是什么?
堆是基于完全二叉树的自适应的数据结构,插入删除会重新调整堆内数据
- 父节点大于等于其子节点的堆叫最大堆
- 父节点小于等于其子节点的堆叫最小堆
使用堆可以实现优先级队列,最大堆添加元素只能添加到尾部,删除元素只能删除头部,增删后会自动调整为最大堆
最大堆实现
- MaxHeap() 创建指定元素数组,后面通过add构建最大堆
- MaxHeap(int[]) 根据传入数组构建最大堆
- add()、remove()增删元素后,需要重新构建最大堆
maxHeapChange()为构建最大堆方法,具体为
- 从右往左从下往上调整所有父节点(非叶节点),数组从0开始,最后一个节点为arr.length - 1,除以2即是最后一个父节点
- 对比父节点和它的左右节点,从左右节点选择大的与父节点比较,若大于则交换
- 交换后递归子树,因为交换后可能导致子树不符合最大堆
sort()采用堆排序返回排好的数组,具体为
- 依次将最后一个节点(叶节点)和根节点交换(即将最大的放在最后)
- 从根节点往下调整节点(交换后不符合最大堆,需重新构建),此次构建不包括最后节点(传入参数为arr.length - 1)
class MaxHeap {
private int[] elements;
private int size;
public MaxHeap() {
elements = new int[7];
size = 0;
}
public MaxHeap(int[] array) {
elements = array;
size = array.length;
maxHeapChange();
}
public void add(int value) {
elements[size] = value;
size++;
maxHeapChange();
}
private void maxHeapChange() {
for (int i = (size - 1) / 2; i >= 0; i--) {
buildMaxHeap(size, i);
}
}
public int remove() {
int temp = elements[0];
elements[0] = elements[size - 1];
elements[size - 1] = 0;
size--;
maxHeapChange();
return temp;
}
public int peek() {
return elements[0];
}
public int[] sort() {
for (int i = size - 1; i > 0; i--) {
int temp = elements[0];
elements[0] = elements[i];
elements[i] = temp;
buildMaxHeap(i, 0);
}
return elements;
}
private void buildMaxHeap(int size, int parent) {
int leftChild = 2 * parent + 1;
int rightChild = 2 * parent + 2;
int max = parent;
if (leftChild < size && elements[leftChild] > elements[max]) {
max = leftChild;
}
if (rightChild < size && elements[rightChild] > elements[max]) {
max = rightChild;
}
if (max != parent) {
int temp = elements[parent];
elements[parent] = elements[max];
elements[max] = temp;
buildMaxHeap(size, max);
}
}
public static void array2CompleteBinaryTree(int[] array) {
int treeDepth = (int) (Math.log(array.length) / Math.log(2)) + 1;
StringBuilder stringBuilder = new StringBuilder();
int space = (int) (Math.pow(2, treeDepth) / 2 - 1);
int move = 0;
for (int i = 0; i < treeDepth; i++) {
for (int j = 0; j < Math.pow(2, i); j++) {
for (int k = 0; k < space; k++) {
stringBuilder.append("*");
}
if (move < array.length) {
if (move == (Math.pow(2, i + 1) - 2)) {
stringBuilder.append(array[move]);
} else {
stringBuilder.append(array[move] + "*");
}
}
for (int k = 0; k < space; k++) {
stringBuilder.append("*");
}
move++;
}
space /= 2;
stringBuilder.append("\n");
}
System.out.println(stringBuilder.toString());
}
@Override
public String toString() {
int treeDepth = (int) (Math.log(elements.length) / Math.log(2)) + 1;
StringBuilder stringBuilder = new StringBuilder();
int space = (int) (Math.pow(2, treeDepth) / 2 - 1);
int move = 0;
for (int i = 0; i < treeDepth; i++) {
for (int j = 0; j < Math.pow(2, i); j++) {
for (int k = 0; k < space; k++) {
stringBuilder.append("*");
}
if (move < elements.length) {
if (move == (Math.pow(2, i + 1) - 2)) {
stringBuilder.append(elements[move]);
} else {
stringBuilder.append(elements[move] + "*");
}
}
for (int k = 0; k < space; k++) {
stringBuilder.append("*");
}
move++;
}
space /= 2;
stringBuilder.append("\n");
}
return stringBuilder.toString();
}
}
array2CompleteBinaryTree()和toString()方法是用于打印树型结构,只能对齐打印10以内的元素,具体为
- 获取数的深度treeDepth
- 获取最后一层数据总量Math.pow(2, treeDepth),除以2减1得到需要对齐打印的星号
- 循环i用于打印每层数据,循环j用于定位move的位置(即要打印的元素),循环k用于打印数据前后的空星号
最大堆测试
如下是基于MaxHeap(int[]) 根据传入数组构建最大堆的测试
int[] array = {1, 7, 3, 2, 9, 8};
System.out.print("原来的数组" + Arrays.toString(array));
System.out.print("转化为如下完全二叉树\n");
MaxHeap.array2CompleteBinaryTree(array);
System.out.println("-------------------");
System.out.print("原来的数组" + Arrays.toString(array));
MaxHeap maxHeap = new MaxHeap(array);
System.out.print("转化为如下最大堆\n");
System.out.println(maxHeap);
System.out.println("-------------------");
System.out.print("利用最大堆排序后的数组");
int[] sortArray = maxHeap.sort();
System.out.print(Arrays.toString(sortArray));
System.out.print("转化为如下完全二叉树\n");
MaxHeap.array2CompleteBinaryTree(sortArray);
System.out.println("-------------------");
打印如下
原来的数组[1, 7, 3, 2, 9, 8]转化为如下完全二叉树
***1***
*7***3*
2*9*8*
-------------------
原来的数组[1, 7, 3, 2, 9, 8]转化为如下最大堆
***9***
*7***8*
2*1*3*
-------------------
利用最大堆排序后的数组[1, 2, 3, 7, 8, 9]转化为如下完全二叉树
***1***
*2***3*
7*8*9*
如下是基于add、move方法的测试
System.out.print("往最大堆尾部添加元素1 7 3 2 9 8");
MaxHeap maxHeap = new MaxHeap();
maxHeap.add(1);
maxHeap.add(7);
maxHeap.add(3);
maxHeap.add(2);
maxHeap.add(9);
maxHeap.add(8);
System.out.print(" 转化为如下最大堆\n");
System.out.println(maxHeap);
System.out.print("从最大堆取出头部元素" + maxHeap.remove() + "后最大堆调整为\n");
System.out.println(maxHeap);
打印如下
往最大堆尾部添加元素1 7 3 2 9 8 转化为如下最大堆
***9***
*7***8*
1*2*3*0
从最大堆取出头部元素9后最大堆调整为
***8***
*7***3*
1*2*0*0
最小堆实现
最小堆和最大堆实现原理相同,只不过是修改比较规则,不再赘述