1. 程式人生 > >Java 排序之選擇排序、堆排序

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;
	}
}