堆可以被視爲一棵完全二叉樹,每個節點的值都比其子節點大。
由於是完全二叉樹,可以方便的用數組來表示堆,以及實現堆的各種操作(插入,取出最大值,建堆)
本代碼用一維數組實現堆
目錄
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("}");
}
}