【轉】堆排序Heap Sort——Java實現
首先,堆是一種資料結構,你可以把他看成一顆完全二叉樹,如下圖所示:圓圈上方的數字代表下標:他的特性就是:父結點的值要大於兩個兒子結點的值。
上圖選自演算法導論,下標從1開始,但我們寫的時候,肯定是要按照從0開始的下標來寫程式碼拉,這一點後面不會再特別說明了。
雖然堆可以用陣列表示,但堆和陣列有所區別,主要是在於陣列的長度(length)不一定等於堆的大小(heapSize)。heapSize <= length。下標大於heapSize但小於length的值都不屬於堆結構。
所以,在java裡先新建一個類來表示堆:沒有使用陣列的原因是,java裡陣列初始化以後就不能再新增元素了,在講解後面內容的時候會有所不方便。ps:最後面有我用陣列方式實現的堆排序
public class Heap {
private ArrayList<Integer> A;
private int heapSize;
public ArrayList<Integer> getA() {
return A;
}
public void setA(ArrayList<Integer> a) {
A = a;
}
public int getHeapSize() {
return heapSize;
}
public void setHeapSize(int heapSize) {
this.heapSize = heapSize;
}
}
很容易得知,結點i的左兒子右兒子或父結點的下標的計算函式
// 左節點下標
public int left(int i) {
return i * 2 + 1;
}
// 右節點下標
public int right(int i) {
return i * 2 + 2;
}
// 父節點下標
public int parent(int i) {
return (i - 1) / 2;
}
要實現堆排序,我們首先得保持堆的性質。(下面用最大堆舉例)
當兒子結點大於父節點的時候,就失去了最大堆的性質,所以在這個時候,我們只要把兒子結點和父結點交換,但是交換以後,被交換的父結點的兒子結點發生了變化,可能會繼續違背最大堆這個性質,所以要遞迴呼叫這個演算法。過程大致如下圖所示:
對2號結點進行最大堆性質的保持
要實現這個過程的程式碼如下:
/**
* 遞迴實現的堆排序
* @param heap 堆
* @param i 當前座標
*/
public void MaxHeapify(Heap heap, int i) {
int l = left(i);
int r = right(i);
int largest = i;
if (l < heap.getHeapSize() && heap.getA().get(l) > heap.getA().get(i)) {
largest = l;
}
if (r < heap.getHeapSize() && heap.getA().get(r) > heap.getA().get(largest)) {
largest = r;
}
if (largest != i) {
int temp = heap.getA().get(i);
heap.getA().set(i, heap.getA().get(largest));
heap.getA().set(largest, temp);
} else
return;
MaxHeapify(heap, largest);
}
其實,這個演算法是可以非遞迴實現的,可以提升效率:
/**
* 非遞迴實現的堆排序
* @param heap 堆
* @param i 當前座標
*/
public void MaxHeapifyNoRecursive(Heap heap, int i) {
while (true) {
int l = left(i);
int r = right(i);
int heapSize = heap.getHeapSize();
ArrayList<Integer> A = heap.getA();
int largest = i;
if (l < heapSize && A.get(l) > A.get(i)) {
largest = l;
}
if (r < heapSize && A.get(r) > A.get(largest)) {
largest = r;
}
if (largest != i) {
int temp = A.get(i);
A.set(i, A.get(largest));
A.set(largest, temp);
} else
return;
i = largest;
}
}
有了上述的演算法,我們就可以進行建堆操作了,建堆的過程很簡單,從下標heapSize - 1開始,對每個結點都執行MaxHeapify就行了,但是葉子結點由於沒有子結點,所以只需要從(heapSize - 1)/2開始,對每個結點都執行MaxHeapify就行了
/**
* 構建最大堆
* @param heap 堆
*/
public void BuildMaxHeap(Heap heap) {
int heapsize = heap.getHeapSize();
for (int i = (heapsize - 1) / 2; i>= 0; i--) {
MaxHeapify(heap, i);
}
}
這個過程大概如下圖所示:
接下來,就是堆排序演算法了。
先用BuildMaxHeap把輸入的陣列A構造成最大堆。然後,把下標heapSize - 1的元素和下標為0的元素對換,通過減小heapSize,讓下標為heapSize - 1的元素從堆中剔除,再呼叫MaxHeapify(heap, 0)即可保證最大堆的性質。重複這個過程,直到堆中只剩下一個元素。
/**
* 堆排序演算法
* @param heap 堆
*/
public void HeapSort(Heap heap) {
BuildMaxHeap(heap);
int length = heap.getA().size(), heapSize = heap.getHeapSize();
for (int i = length - 1; i > 0; i--) {
int temp = heap.getA().get(i);
heap.getA().set(i, heap.getA().get(0));
heap.getA().set(0,temp);
heap.setHeapSize(--heapSize);
MaxHeapify(heap, 0);
}
}
這個過程的圖示如下:
這樣堆排序演算法就算完成了,複雜度僅為O(nlg(n)),但是,其實快速排序往往優於它,雖然複雜度和它一樣。
宣告:此為轉發博文,原博文地址:https://blog.csdn.net/sunnylinner/article/details/52585225
在此配上我用陣列實現的堆排序:
package arithmetic;
import java.util.Arrays;
/**
* 堆排序java實現
*/
public class HeapSort {
static int k = 0; //全域性k,用於排序一次後獲得一個最大的,需要陣列的size-1
public static void main(String[] args) {
int[] datas = {4,2,87,4,2,7,9,6,3,7};
System.out.println(Arrays.toString(datas));
//初始化堆
buildHeap(datas);
//進行排序
for (int i = datas.length-1; i > 0 ; i--) {
//堆頂的最大值與陣列的最後一個元素交換(此處為k存在的原因)
int temp = datas[i];
datas[i] = datas[0];
datas[0] = temp;
k++; //使陣列的size-1
//調整堆
adjustHeap(datas,0);
}
System.out.println(Arrays.toString(datas));
}
/**
* 初始化堆
* @param datas 需被初始化的陣列
*/
static void buildHeap(int[] datas){
//(datas.length-1)>>1 從最底層的上一層開始(從最後有孩子節點的一層開始)
for (int i = (datas.length-1)>>1; i >= 0; i--) {
adjustHeap(datas,i);
}
}
/**
* 調整堆
* @param datas 需被調整的堆
* @param i 調整元素所在的位置
*/
static void adjustHeap(int[] datas , int i){
//迴圈,使可以調整到底部:因為如果上層調整,可能會影響到下層,所以要while知道該層下面的資料都滿足條件
while(true){
int left = (i<<1)+1; //左孩子
int right = (i<<1)+2; //右孩子
int largest = i; //標識自身和孩子中,最大值的下標
if (left < datas.length-k && datas[i] < datas[left]){
largest = left;
}
if (right < datas.length-k && datas[largest] < datas[right]){
largest = right;
}
//如果滿足條件,表示最大的不是節點自身,則交換
if (largest != i){
int temp = datas[i];
datas[i] = datas[largest];
datas[largest] = temp;
}else{ //否則,退出迴圈
// 原因:因為是從最下面開始進行調整的,所以我們可以只要最大節點是節點自身,我們就可以直接退出
return;
}
i = largest; //修改自身節點
}
}
}
相關文章
- 堆排序 Heap Sort排序
- [PY3]——heap模組 和 堆排序排序
- 實現堆排序排序
- Java實現HEAPSORT堆排序演算法Java排序演算法
- js 實現堆排序JS排序
- 堆排序(php實現)排序PHP
- 堆排序(實現c++)排序C++
- 使用 Swift 實現堆排序Swift排序
- 堆排序c++實現排序C++
- [JAVA]堆排序Java排序
- 用java語言用sort方法實現groupby分組Java
- 利用java實現插入排序、歸併排序、快排和堆排序Java排序
- PHP 實現堆, 堆排序以及索引堆PHP排序索引
- 觸發 Java heap dumpJava
- 利用dbms_redefinition實現普通heap表和分割槽表的轉化
- java.lang.OutOfMemoryError: Java heap spaceJavaError
- 常見演算法 PHP 實現 -- 堆排序演算法PHP排序
- 大根堆和堆排序的原理與實現排序
- java資料結構基礎-利用Heap(堆)實現PriorityQueue(優先佇列)Java資料結構佇列
- PAT乙級——1092(陣列排序 自定義sort)Java實現陣列排序Java
- Java 實現 markdown轉HtmlJavaHTML
- kettle java.lang.OutOfMemoryError: Java heap spaceJavaError
- Java Arrays.sort()Java
- 資料結構&堆&heap&priority_queue&實現資料結構
- Flink Sort-Shuffle 實現簡介
- 使用sort方法實現陣列升序降序陣列
- .NET 排序 Array.Sort<T> 實現分析排序
- java - OutOfMemoryError: Java heap space 堆空間不足JavaError
- java 實現文字格式轉換Java
- 關於GWT的Java heap spaceJava
- Java堆記憶體Heap與非堆記憶體Non-HeapJava記憶體
- java.lang.OutOfMemoryError: Java heap space的解決JavaError
- mysql 報錯:java.lang.OutOfMemoryError: Java heap spaceMySqlJavaError
- java實現連結串列反轉Java
- java實現url轉碼、解碼Java
- 用 Java 實現回撥例程 (轉)Java
- Java Web Start 實現關鍵 (轉)JavaWeb
- Heap Block Compress現象分析BloC