堆(heap)
-
堆是計算機科學中一類特殊的數據結構統稱,堆通常是一個可以被看做一棵樹的數組對象
-
特點:
- 堆中的某個節點總是不大於或不小於其父節點的值
- 堆總是一顆完全二叉樹
-
將根節點最大的堆叫做最大堆或大根堆,根節點最小的堆成爲最小堆或者小根堆
-
二叉堆是一顆完全二叉樹
-
完全二叉樹:除了最後一層外,其他各層的節點數都達到了最大個數,第h層的所有結點都連續集中在最左邊,這就是完全二叉樹
-
堆:就是以樹的結構進行表達
-
不過,我認爲堆需要注意的是它的結構,如果要向堆中添加一個元素,那麼我們需要的操作是,首先將這個元素插入堆的底部,然後我們還要進行解決的是與父母節點的交換問題,最大堆和最小堆的結構需要進行解釋
//向堆中添加元素 public void add(E e){ data.addLast(e); siftUp(data.getSize()-1); } private void siftUp(int k){ while (k>0&&data.get(parent(k)).compareTo(data.get(k))<0){ data.swap(k,parent(k)); k=parent(k); } }
-
向堆中取出最大元素
//取出堆中最大的元素 public E extractMax(){ E ret = findMax(); data.swap(0,data.getSize()-1); data.removeLast(); siftDown(0); return ret; } private void siftDown(int k){ while (leftChild(k)<data.getSize())//這裏我們畫圖就可以理解,如果左孩子大於整個數組的長度的話, //我們可以理解爲左孩子沒有 { int j = leftChild(k); if (j+1<data.getSize()&&data.get(j+1).compareTo(data.get(j))>0) j = rightChild(k);//data[j]必須是左孩子和右孩子中的最大值 if(data.get(k).compareTo(data.get(j))>=0) break; data.swap(k,j); k=j; } }
-
replace方法:取出最大元素後,放入一個新的元素
-
可以先extractMax,再add,兩次O(logn)的操作
-
可以直接將堆頂元素替換以後下沉,一次O(logn)的操作
//取出堆中最大的元素,並且替換成元素e public E replace(E e) { E ret = findMax(); data.set(0,e); siftDown(0); return ret; }
-
-
heapify:將任意數組整理成堆的形狀
-
我們可以直接遍歷一個數組,然後將數組中的元素直接添加進一個新創建的就行
-
更快的方法,找到最後一個非葉子節點,不斷進行下沉操作就行
//將任意數組整理成堆 public Maxheap(E[] arr){ data = new Array<>(arr); for (int i=parent(arr.length-1);i>=0;i--) siftDown(i); }
-
-
完整代碼:
package 數據結構.MaxHeap; import 數據結構.棧.Array; public class Maxheap<E extends Comparable<E>> { private Array<E> data; public Maxheap(int capacity){ data = new Array<>(capacity); } public Maxheap(){ data = new Array<>(); } //返回堆中所有元素個數 public int size(){ return data.getSize(); } //確定堆是否爲空 public boolean isEmpty(){ return data.isEmpty(); } //返回完全二叉樹的數組表示中,一個索引所表示元素的父親節點的索引 private int parent(int index){ if(index==0) throw new IllegalArgumentException("index-0 doesn't hava parent"); return (index-1)/2; } //返回完全二叉樹的數組表示中,一個索引所表示的元素的左孩子節點的索引 private int leftChild(int index){ return index*2 + 1; } //返回完全二叉樹的數組表示中,一個索引所表示的元素的右孩子節點的索引 private int rightChild(int index){ return index*2+2; } //向堆中添加元素 public void add(E e){ data.addLast(e); siftUp(data.getSize()-1); } private void siftUp(int k){ while (k>0&&data.get(parent(k)).compareTo(data.get(k))<0){ data.swap(k,parent(k)); k=parent(k); } } //查找堆中最大元素 public E findMax(){ if(data.getSize()==0) throw new IllegalArgumentException("Can not findMax when heap is,,,,"); return data.get(0); } //取出堆中最大的元素 public E extractMax(){ E ret = findMax(); data.swap(0,data.getSize()-1); data.removeLast(); siftDown(0); return ret; } private void siftDown(int k){ while (leftChild(k)<data.getSize())//這裏我們畫圖就可以理解,如果左孩子大於整個數組的長度的話, //我們可以理解爲左孩子沒有 { int j = leftChild(k); if (j+1<data.getSize()&&data.get(j+1).compareTo(data.get(j))>0) j = rightChild(k);//data[j]必須是左孩子和右孩子中的最大值 if(data.get(k).compareTo(data.get(j))>=0) break; data.swap(k,j); k=j; } } //取出堆中最大的元素,並且替換成元素e public E replace(E e) { E ret = findMax(); data.set(0,e); siftDown(0); return ret; } //將任意數組整理成堆 public Maxheap(E[] arr){ data = new Array<>(arr); for (int i=parent(arr.length-1);i>=0;i--) siftDown(i); } }
優先隊列
-
普通的隊列是一種先進先出的數據結構,元素在隊列尾追加,而從隊列頭刪除。在優先隊列中,元素被賦予優先級。當訪問元素時,具有最高優先級的元素最先刪除。優先隊列具有最高級先出 (first in, largest out)的行爲特徵。通常採用堆數據結構來實現
-
可以使用最大堆來進行測試
package 數據結構.MaxHeap; public interface Queue<E> { int getSize(); boolean isEmpty(); void enqueue(E e); E dequeue(); E getFront(); } package 數據結構.MaxHeap; public class PriorityQueue<E extends Comparable<E>> implements Queue<E> { private Maxheap<E> maxheap; public PriorityQueue(){ maxheap = new Maxheap<>(); } @Override public int getSize() { return maxheap.size(); } @Override public boolean isEmpty() { return maxheap.isEmpty(); } @Override public E getFront() { return maxheap.findMax(); } @Override public void enqueue(E e) { maxheap.add(e); } @Override public E dequeue() { return maxheap.extractMax(); } }