0,堆的簡介
數據結構中的堆是一種特殊的二叉樹,不同於 Java 內存模型中的堆。
堆必須符合以下兩個條件:
- 是一棵完全二叉樹。
- 任意一個節點的值都大於(或小於)左右子節點的值。
從第一點可以知道,堆適合用數組來存儲。
第二點中,若父節點都大於等於左右子節點,則被稱為大頂堆
,反之則為小頂堆
。
圖-最大堆及其存儲方式
0.1節點的父、子節點關系
一個節點【根節點除外】的父節點地址為其地址的二分之一,它的左子節點地址為其地址值的2倍,右子節點地址為其地址2倍加1。
例如:現在有個節點的地址為3,其數值為5;那么它的父節點地址為3/2=1,父節點的數值為13;左子節點地址為3*2=6,左子節點數值為6;右子節點地址為3*2+1=7,這個最大堆中沒有其右節點數據。
0.2節點的存儲、刪除操作
添加新元素的時候,一般先存放到數組的尾部,之后在通過向上重排序的操作,來進行堆化【滿足堆數據結構的調整】
刪除元素時【一般默認刪除第一個根節點】,現將數組的最后一個元素放到根節點的位置,之后通過向下重排序的操作,來進行堆化處理。
1,堆的實現
主要實現功能:①添加元素-add();②輸出極值並清除-poll();③輸出極值不清除-peek();
上面的功能都是是顯示的函數,隱形函數有:①擴容dilatate();②向上重排序reSortUp();③向下重排序reSortDown();
package com.cnblogs.mufasa.Solution1; public class Heap { private static final int CAPACITY=16; private static final boolean TYPE=true; private static int[] nums; private int capacity=16; int size=0; private boolean type=true;//true由小到大,false由大到小 public Heap(){ this(CAPACITY); } public Heap(int capacity){ this(capacity,TYPE); } public Heap(boolean type){ this(CAPACITY,type); } public Heap(int capacity,boolean type){ this.capacity=capacity; this.type=type; nums=new int[capacity]; } //數據添加 public void add(int num){ if(size+1>=capacity){ dilatate(); } nums[size+1]=num; reSortUp(size+1); size++; } private void reSortUp(int index){ if(type){//由小到大 while (index!=1){ if(nums[index/2]>nums[index]){ int temp=nums[index]; nums[index]=nums[index/2]; nums[index/2]=temp; index/=2; }else if(nums[index/2]==nums[index]){ // throw new IllegalArgumentException("數據結構-堆不接受重復數據輸入"); break; }else { return; } } }else {//由大到小 while (index!=1){ if(nums[index/2]<nums[index]){ int temp=nums[index]; nums[index]=nums[index/2]; nums[index/2]=temp; index/=2; }else if(nums[index/2]==nums[index]){ // throw new IllegalArgumentException("數據結構-堆不接受重復數據輸入"); break; }else { return; } } } } //數據輸出,並且清楚該數據 public int poll() throws Exception { if(size>0){ int temp=nums[1]; nums[1]=nums[size]; reSortDown(); size--; return temp; }else { throw new Exception("數據為空"); } } private void reSortDown(){ int index=1; int L,R; if(type){//由小到大 while (index<size){ L=index*2; R=L+1; if(R<=size){ boolean flag=nums[L]<nums[R]; int min=(flag?nums[L]:nums[R]); if(nums[index]>min){ if(flag){ int temp=nums[index]; nums[index]=nums[L]; nums[L]=temp; index=L; }else { int temp=nums[index]; nums[index]=nums[R]; nums[R]=temp; index=R; } }else { return; } }else if(L<=size){ if(nums[index]>nums[L]){ int temp=nums[index]; nums[index]=nums[L]; nums[L]=temp; } return; }else { return; } } }else {//由大到小 while (index<size){ L=index*2; R=L+1; if(R<size){ boolean flag=nums[L]<nums[R]; int max=(flag?nums[R]:nums[L]); if(nums[index]<max){ if(flag){ int temp=nums[index]; nums[index]=nums[R]; nums[R]=temp; index=R; }else { int temp=nums[index]; nums[index]=nums[L]; nums[L]=temp; index=L; } }else { return; } }else if(L<size){ if(nums[index]<nums[L]){ int temp=nums[index]; nums[index]=nums[L]; nums[L]=temp; } return; }else { return; } } } } //數據輸出,不清除該數據 public int peek() throws Exception { if(size>0){ return nums[0]; }else { throw new Exception("數據為空"); } } //數據擴容,二倍擴容 private void dilatate(){ capacity=capacity<<1; int[] pre=new int[capacity]; for(int i=1;i<=size;i++){ pre[i]=nums[i]; } nums=pre; } } class Client{ public static void main(String[] args) throws Exception { Heap heap=new Heap(4,true); // Heap heap=new Heap(4,false); heap.add(5); heap.add(3); heap.add(3); heap.add(7); heap.add(1); heap.add(0); heap.add(8); heap.add(8); int len=heap.size; for(int i=0;i<len;i++){ System.out.print(heap.poll()+","); } } } /* 0,1,3,5,7,8, 8,7,5,3,1,0, */
2,堆的應用
3.1 堆排序
利用堆這種數據結構,來進行數組排序,時間復雜度為O(nlogn)
3.2 Java容器中的優先隊列
PriorityQueue<Integer> queue=new PriorityQueue<Integer>();
3.3 求動態集合中位數
有若干個數據,求其中位數,並且數據還在不斷的輸入【兩個堆數據即可很好的解決問題,一個最大堆,一個最小堆】
3.4 60百分位的數
這個問題是上面的問題的擴展,本質上也是使用兩個對數據結構即可。但是需要控制兩個堆中元素個數的比例!