Java 排序之選擇排序、堆排序
三、選擇排序
1. 演算法簡介
選擇排序相對於上一篇文章記錄的插入排序、希爾排序要簡單一些,它比較直觀。它的基本思路為:把第一個元素依次和後面的所有元素進行比較,第一次結束後,就會有最小值出現在最前面,依次類推。
2. 演算法分析
假設有一個數組:
int[] arr = { 54, 40, 60, 55, 52 };
- 用第零個元素
54
先和40
相比,40
小,54
放到原來40
的位置,用40
繼續和其他的比,沒有比它小的了,第一輪結束,這時的順序為:
{ 40, 54, 60, 55, 52 };
- 用第一個元素
54
和後面的進行比較,過程同上,有比它小的就把小的拿出來,把它放進去,用小的繼續比 - 依次類推…
3. 演算法圖解附視訊
視訊 點此檢視。
4. 演算法程式碼
import java.util.Arrays;
public class SelectionSort {
public static void main(String[] args) {
int[] arr = { 54, 40, 60, 55, 52 };
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr) {
for (int x = 0; x < arr. length - 1; x++) {
for (int y = x + 1; y < arr.length; y++) {
if (arr[y] < arr[x]) {
int temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
}
}
}
}
四、堆排序
1. 演算法簡介
堆排序(Heapsort)是經典的排序演算法之一,在面試的時候比較常見,堆排其實是一個看起來複雜其實並不複雜的排序演算法。利用堆積樹(堆)這種資料結構所設計的一種排序演算法,它也是一種選擇排序演算法。可以利用陣列的特點快速定位指定索引的元素,堆分為大根堆和小根堆,近似 完全二叉樹
2. 演算法分析
先一步步瞭解其中的各個方面。
1) 什麼是堆?
在此處提到的堆一般都是指 二叉堆,它滿足兩個特性:
- 父節點的鍵值大於或等於(小於或等於)任何一個子節點的鍵值
- 每個節點的左子樹和右子樹都是一個二叉堆(都是最大堆或最小堆)
下圖為最大堆和最小堆:
2) 什麼是堆調整?
這是為了保持堆的特性而作出的一個操作,對某一個節點為根的子樹做堆調整,就是將根節點進行 下沉 操作(通過交換完成),一直下沉到合適的位置,使得子樹滿足堆的性質。
例如:(最大堆的調整)
- 在對應的陣列元素
A[i]
,左邊子節點A[Left(i)]
和右邊子節點A[Right(i)]
中找到最大的一個,將其下標儲存在 largest 中; - 如果
A[i]
已經是最大的元素,則程式結束; - 否則,
i
的某個子節點為最大的元素,將A[largest]
與A[i]
交換; - 再從交換的子節點開始,重複上面的步驟。
一般做一次堆調整的時間複雜度為:log(n)
下圖為對元素 3
為根節點的子樹做一次堆調整的示意圖:
3) 如何建堆?
建堆是通過不斷地堆調整,使得整個二叉樹中的數滿足堆的特性。在陣列中,一般從下標為 n/2
的數開始做調整,一直到下標為 0 的數(因為下標大於 n/2
的數都是葉子節點,其子樹已經滿足堆的特性)。下圖為一個示例:
3) 如何進行堆排序?
堆排序是在上述 3 中對陣列建堆的操作之後完成的。
陣列儲存成堆得形式後,第一次將 A[0](即堆頂)
與最後一個記錄 A[n-1]
交換,由此得到一個新的無序區 A[0...n-2]
和一個有序區 A[n-1]
。再對 A[0...n-2]
重新恢復堆,第二次將 A[0]
與 A[n-2]
交換,再對 A[0...n-3]
重新恢復堆,重複這樣得操作,直到 A[0]
與 A[1]
交換。由於每次都是將最小的資料併入到後面的有序區間,故操作完成後整個陣列就有序了。
如下圖所示:
3. 演算法程式碼
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] data = { 11, 22, 10, 55, 44 };
sort(data);
System.out.println(Arrays.toString(data));
}
public static void sort(int[] data) {
MaxHeap h = new MaxHeap();
h.init(data);
for (int i = 0; i < data.length; i++)
h.remove();
System.arraycopy(h.queue, 1, data, 0, data.length);
}
private static class MaxHeap {
void init(int[] data) {
this.queue = new int[data.length + 1];
for (int i = 0; i < data.length; i++) {
queue[++size] = data[i];
fixUp(size);
}
}
private int size = 0;
private int[] queue;
public void remove() {
swap(queue, 1, size--);
fixDown(1);
}
// fixdown
private void fixDown(int k) {
int j;
while ((j = k << 1) <= size) {
if (j < size && queue[j] < queue[j + 1])
j++;
if (queue[k] > queue[j]) // 不用交換
break;
swap(queue, j, k);
k = j;
}
}
private void fixUp(int k) {
while (k > 1) {
int j = k >> 1;
if (queue[j] > queue[k])
break;
swap(queue, j, k);
k = j;
}
}
}
/*
* 交換陣列中的兩個元素
*/
public static void swap(int[] data, int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
}