在Java數據結構和算法(五)——隊列中咱們介紹了優先級隊列,優先級隊列是一種抽象數據類型(ADT),它提供了刪除最大(或最小)關鍵字值的數據項的方法,插入數據項的方法,優先級隊列能夠用有序數組來實現,這種實現方式儘管刪除最大數據項的時間複雜度爲O(1),可是插入仍是須要較長的時間 O(N),由於每次插入平均須要移動一半的數據項,來保證插入後,數組依舊有序。html
本篇博客咱們介紹另一種數據結構——堆,注意這裏的堆和咱們Java語言,C++語言等編程語言在內存中的「堆」是不同的,這裏的堆是一種樹,由它實現的優先級隊列的插入和刪除的時間複雜度都爲O(logN),這樣儘管刪除的時間變慢了,可是插入的時間快了不少,當速度很是重要,並且有不少插入操做時,能夠選擇用堆來實現優先級隊列。java
①、它是徹底二叉樹,除了樹的最後一層節點不須要是滿的,其它的每一層從左到右都是滿的。注意下面兩種狀況,第二種最後一層從左到右中間有斷隔,那麼也是不徹底二叉樹。node
②、它一般用數組來實現。算法
這種用數組實現的二叉樹,假設節點的索引值爲index,那麼:編程
節點的左子節點是 2*index+1,數組
節點的右子節點是 2*index+2,數據結構
節點的父節點是 (index-1)/2。數據結構和算法
③、堆中的每個節點的關鍵字都大於(或等於)這個節點的子節點的關鍵字。編程語言
這裏要注意堆和前面說的二叉搜索樹的區別,二叉搜索樹中全部節點的左子節點關鍵字都小於右子節點關鍵字,在二叉搜索樹中經過一個簡單的算法就能夠按序遍歷節點。可是在堆中,按序遍歷節點是很困難的,如上圖所示,堆只有沿着從根節點到葉子節點的每一條路徑是降序排列的,指定節點的左邊節點或者右邊節點,以及上層節點或者下層節點因爲不在同一條路徑上,他們的關鍵字可能比指定節點大或者小。因此相對於二叉搜索樹,堆是弱序的。post
前面咱們說了,堆是弱序的,因此想要遍歷堆是很困難的,基本上,堆是不支持遍歷的。
對於查找,因爲堆的特性,在查找的過程當中,沒有足夠的信息來決定選擇經過節點的兩個子節點中的哪個來選擇走向下一層,因此也很難在堆中查找到某個關鍵字。
所以,堆這種組織彷佛很是接近無序,不過,對於快速的移除最大(或最小)節點,也就是根節點,以及能快速插入新的節點,這兩個操做就足夠了。
移除是指刪除關鍵字最大的節點(或最小),也就是根節點。
根節點在數組中的索引老是0,即maxNode = heapArray[0];
移除根節點以後,那樹就空了一個根節點,也就是數組有了一個空的數據單元,這個空單元咱們必須填上。
第一種方法:將數組全部數據項都向前移動一個單元,這比較費時。
第二種方法:
①、移走根
②、把最後一個節點移動到根的位置
③、一直向下篩選這個節點,直到它在一個大於它的節點之下,小於它的節點之上爲止。
具體步驟以下:
圖a表示把最後一個節點移到根節點,圖b、c、d表示將節點向下篩選到合適的位置,它的合適位置在最底層(有時候可能在中間),圖e表示節點在正確位置的情景。
注意:向下篩選的時候,將目標節點和其子節點比較,誰大就和誰交換位置。
插入節點也很容易,插入時,選擇向上篩選,節點初始時插入到數組最後第一個空着的單元,數組容量大小增一。而後進行向上篩選的算法。
注意:向上篩選和向下不一樣,向上篩選只用和一個父節點進行比較,比父節點小就中止篩選了。
首先咱們要知道用數組表示堆的一些要點。若數組中節點的索引爲x,則:
節點的左子節點是 2*index+1,
節點的右子節點是 2*index+2,
節點的父節點是 (index-1)/2。
注意:"/" 這個符號,應用於整數的算式時,它執行整除,且獲得是是向下取整的值。
package com.ys.tree.heap; public class Heap { private Node[] heapArray; private int maxSize; private int currentSize; public Heap(int mx) { maxSize = mx; currentSize = 0; heapArray = new Node[maxSize]; } public boolean isEmpty() { return (currentSize == 0)? true : false; } public boolean isFull() { return (currentSize == maxSize)? true : false; } public boolean insert(int key) { if(isFull()) { return false; } Node newNode = new Node(key); heapArray[currentSize] = newNode; trickleUp(currentSize++); return true; } //向上調整 public void trickleUp(int index) { int parent = (index - 1) / 2; //父節點的索引 Node bottom = heapArray[index]; //將新加的尾節點存在bottom中 while(index > 0 && heapArray[parent].getKey() < bottom.getKey()) { heapArray[index] = heapArray[parent]; index = parent; parent = (parent - 1) / 2; } heapArray[index] = bottom; } public Node remove() { Node root = heapArray[0]; heapArray[0] = heapArray[--currentSize]; trickleDown(0); return root; } //向下調整 public void trickleDown(int index) { Node top = heapArray[index]; int largeChildIndex; while(index < currentSize/2) { //while node has at least one child int leftChildIndex = 2 * index + 1; int rightChildIndex = leftChildIndex + 1; //find larger child if(rightChildIndex < currentSize && //rightChild exists? heapArray[leftChildIndex].getKey() < heapArray[rightChildIndex].getKey()) { largeChildIndex = rightChildIndex; } else { largeChildIndex = leftChildIndex; } if(top.getKey() >= heapArray[largeChildIndex].getKey()) { break; } heapArray[index] = heapArray[largeChildIndex]; index = largeChildIndex; } heapArray[index] = top; } //根據索引改變堆中某個數據 public boolean change(int index, int newValue) { if(index < 0 || index >= currentSize) { return false; } int oldValue = heapArray[index].getKey(); heapArray[index].setKey(newValue); if(oldValue < newValue) { trickleUp(index); } else { trickleDown(index); } return true; } public void displayHeap() { System.out.println("heapArray(array format): "); for(int i = 0; i < currentSize; i++) { if(heapArray[i] != null) { System.out.print(heapArray[i].getKey() + " "); } else { System.out.print("--"); } } } } class Node { private int iData; public Node(int key) { iData = key; } public int getKey() { return iData; } public void setKey(int key) { iData = key; } }