堆可以被视为一棵完全二叉树,每个节点的值都比其子节点大。
由于是完全二叉树,可以方便的用数组来表示堆,以及实现堆的各种操作(插入,取出最大值,建堆)
本代码用一维数组实现堆
目录
1.编写测试程序
测试插入
import java.util.*;
public class test {
public static void main(String[] args) {
{// 测试插入
MaxHeap test1 = new MaxHeap();
System.out.println(test1.insert(100));
System.out.println(test1.insert(50));
System.out.println(test1.insert(51));
System.out.println(test1.insert(200));
System.out.println(test1.insert(70));
System.out.println(test1.insert(150));
System.out.println(test1.insert(300));
System.out.println("{300,100,200,50,70,51,150}:answer should be ");
test1.printMaxHeap();
System.out.println();
for(int i = 0; i < 9; i++) {
if (i != 0) {
System.out.print(",");
}
test1.deleteMax();
}
System.out.println();
System.out.println();
}
{// 测试取出最大值
MaxHeap test1 = new MaxHeap();
test1.printMaxHeap();
System.out.println();
test1.deleteMax();
System.out.print(",");
System.out.println();
System.out.println();
}
{// 用数组建堆(之前测试的一开始是空堆)
int[] testArray = {16,21,75,38,2,50,85,19,61,82};
MaxHeap test1 = new MaxHeap(testArray);
test1.printMaxHeap();
System.out.println();
test1.deleteMax();
}
}
}
2.变量初始化,创建构造器
两种建堆方式,一种是建空堆,一种是用已知数据建堆(先把数据载入堆,再调用建堆方法)
class MaxHeap {
int maxSize = 100;//堆的最大大小
int[] array = new int[maxSize+1];
int nowSize = 0;//目前堆使用的部分的大小
public MaxHeap() {
array[0] = Integer.MAX_VALUE;
}
public MaxHeap(int[] inputArray) {
int inputArrayLength = inputArray.length;
nowSize = inputArrayLength;
array[0] = Integer.MAX_VALUE;
for (int i = 1; i <= inputArrayLength; i++) {
array[i] = inputArray[i-1];
}
buildMaxHeap();
}
//其它方法
}
3.插入操作
先将数字插至数组末尾新增位,然后不断和其父节点比较,如果比父节点大则换位置
public boolean insert (int data) {
if (nowSize == maxSize) {
System.out.println("最大堆已满");
return false;
}
nowSize++;
int i = nowSize;
for (;array[i/2] < data; i = i/2) {// 完全二叉树 n/2是父节点
array[i] = array[i/2];// 和插入排序的移动方式类似
}
array[i] = data;
return true;
}
4.取出最大值操作
1.先把根节点赋给临时变量,然后把数组最后的值移至根节点(赋值),对该值用建树时的排位方法处理,使得整棵树变回大根堆
public void deleteMax() {
if (nowSize == 0) {
System.out.print("最大堆已为空");
return;
}
int maxData = array[1];// 取出根节点最大值
array[1] = array[nowSize];//将最后值赋给根节点
nowSize--;
buildFromTreeRoot(1);//对根节点值进行处理
System.out.print(maxData);
return;//这里可以返回maxData
}
5.数据建堆操作
建堆有两种方法,一种是一个个数据插入空堆,则时间复杂度为O(n*logn)。一种是先把数据放入堆,在对每个父子节点进行比较,再处理位置。复杂度为O(n)。
先把每颗树的子树先处理完再处理根节点,把这个过程迭代下去。最后每个节点所需的调整次数为其高度减一
public void buildMaxHeap() {
for(int i = nowSize / 2; i > 0; i--) {
buildFromTreeRoot(i);
}
}
6.将根节点与两边比较和移动的方法
这里的根节点可以是子树的根节点
不断的和子节点中最大的比较,若小於则交换
//将根节点与两边比较和移动的方法
public void buildFromTreeRoot(int p) {
int temp = array[p];//先用临时变量存下树顶值
int Parent;
int Child;
for (Parent = p ; Parent * 2 <= nowSize ; Parent = Child) {
Child = Parent * 2;
/* 开始循环说了Parent * 2 <= nowSize,且Child = Parent * 2且Child != nowSize时,那么可以肯定Child+1<=nowSize。
* 那么就可以比较Child和Child++的大小了,反之如果且Child == nowSize,那么Child==nowSize那么自然后面的不用考虑。
*/
if ((Child != nowSize) && array[Child] < array[Child + 1]) {
Child++;//Child指向左右子节点中的最大者
}
if ( temp >= array[Child] ) {
break;
} else {
array[Parent] = array[Child];
}
}
//跳出时没有子结点,或者比子结点大可以跳出了
array[Parent] = temp;
}
7.层序遍历
用于测试
public void printMaxHeap() {//打印数组相当于层序遍历
System.out.print("{");
int i;
for(i = 1; i <= nowSize; i++) {
if (i != 1) {
System.out.print(",");
}
System.out.print(array[i]);
}
System.out.print("}");
}
8.全部代码
package maxHeap;
import java.util.*;
public class test {
public static void main(String[] args) {
/*{
MaxHeap test1 = new MaxHeap();
System.out.println(test1.insert(100));
System.out.println(test1.insert(50));
System.out.println(test1.insert(51));
System.out.println(test1.insert(200));
System.out.println(test1.insert(150));
System.out.println("{200,150,51,50,100}:answer should be ");
test1.printMaxHeap();
System.out.println();
}
{
MaxHeap test1 = new MaxHeap();
System.out.println(test1.insert(100));
System.out.println(test1.insert(50));
System.out.println(test1.insert(51));
System.out.println(test1.insert(200));
System.out.println(test1.insert(70));
System.out.println(test1.insert(150));
System.out.println("{200,100,150,50,70,51}:answer should be ");
test1.printMaxHeap();
System.out.println();
}*/
{// 测试插入
MaxHeap test1 = new MaxHeap();
System.out.println(test1.insert(100));
System.out.println(test1.insert(50));
System.out.println(test1.insert(51));
System.out.println(test1.insert(200));
System.out.println(test1.insert(70));
System.out.println(test1.insert(150));
System.out.println(test1.insert(300));
System.out.println("{300,100,200,50,70,51,150}:answer should be ");
test1.printMaxHeap();
System.out.println();
for(int i = 0; i < 9; i++) {
if (i != 0) {
System.out.print(",");
}
test1.deleteMax();
}
System.out.println();
System.out.println();
}
{// 测试取出最大值
MaxHeap test1 = new MaxHeap();
test1.printMaxHeap();
System.out.println();
test1.deleteMax();
System.out.print(",");
System.out.println();
System.out.println();
}
{// 测试生成大根堆就载入数组
int[] testArray = {16,21,75,38,2,50,85,19,61,82};
MaxHeap test1 = new MaxHeap(testArray);
test1.printMaxHeap();
System.out.println();
test1.deleteMax();
}
}
}
class MaxHeap {
int maxSize = 100;//堆的最大大小
int[] array = new int[maxSize+1];
int nowSize = 0;//目前堆使用的部分的大小
public MaxHeap() {
array[0] = Integer.MAX_VALUE;
}
public MaxHeap(int[] inputArray) {
int inputArrayLength = inputArray.length;
nowSize = inputArrayLength;
array[0] = Integer.MAX_VALUE;
for (int i = 1; i <= inputArrayLength; i++) {
array[i] = inputArray[i-1];
}
buildMaxHeap();
}
public boolean insert (int data) {
if (nowSize == maxSize) {
System.out.println("最大堆已满");
return false;
}
nowSize++;
int i = nowSize;
for (;array[i/2] < data; i = i/2) {// 完全二叉树 n/2是父节点
array[i] = array[i/2];// 和插入排序的移动方式类似
}
array[i] = data;
return true;
}
public void deleteMax() {
if (nowSize == 0) {
System.out.print("最大堆已为空");
return;
}
int maxData = array[1];// 取出根节点最大值
array[1] = array[nowSize];
nowSize--;
buildFromTreeRoot(1);
System.out.print(maxData);
return;//这里可以返回maxData
}
public void buildMaxHeap() {
//先理子树,子树理完再理根,这样时间复杂度只用O(n)
for(int i = nowSize / 2; i > 0; i--) {
buildFromTreeRoot(i);
}
}
//将根节点与两边比较和移动的函数
public void buildFromTreeRoot(int p) {
int temp = array[p];
int Parent;
int Child;
for (Parent = p ; Parent * 2 <= nowSize ; Parent = Child) {
Child = Parent * 2;
/* 开始循环说了Parent * 2 <= nowSize,且Child = Parent * 2且Child != nowSize时,那么可以肯定Child+1<=nowSize。
* 那么就可以比较Child和Child++的大小了,反之如果且Child == nowSize,那么Child==nowSize那么自然后面的不用考虑。
*/
if ((Child != nowSize) && array[Child] < array[Child + 1]) {
Child++;//Child指向左右子节点中的最大者
}
if ( temp >= array[Child] ) {
break;
} else {
array[Parent] = array[Child];
}
}
//跳出时没有子结点,或者比子结点大可以跳出了
array[Parent] = temp;
}
/**
* 层序遍历
*/
public void printMaxHeap() {//打印数组相当于层序遍历
System.out.print("{");
int i;
for(i = 1; i <= nowSize; i++) {
if (i != 1) {
System.out.print(",");
}
System.out.print(array[i]);
}
System.out.print("}");
}
}