十大排序算法之堆排序

本文首發於我的博客html

前言

本系列排序包括十大經典排序算法。git

  • 使用的語言爲:Java
  • 結構爲: 定義抽象類Sort裏面實現了,交換,大小比較等方法。例如交換兩個值,直接傳入下標就能夠了。其餘的具體排序的類都繼承抽象類Sort。這樣咱們就能專一於算法自己。
/*
	 * 返回值等於0,表明 array[i1] == array[i2]
	 * 返回值小於0,表明 array[i1] < array[i2]
	 * 返回值大於0,表明 array[i1] > array[i2]
	 */
	protected int cmp(int i1, int i2) {
		return array[i1].compareTo(array[i2]);
	}
	
	protected int cmp(T v1, T v2) {
		return v1.compareTo(v2);
	}
	
	protected void swap(int i1, int i2) {
		T tmp = array[i1];
		array[i1] = array[i2];
		array[i2] = tmp;
	}

複製代碼

什麼是堆排序

  • 堆排序(Heap Sort)堆排序(英語:Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆是一個近似徹底二叉樹的結構,並同時知足堆積的性質:即子結點的鍵值或索引老是小於(或者大於)它的父節點。

堆的一些性質和結論

由於本文不是專門講解堆這種數據結構的,主要是講解堆排序算法的。因此這裏只給出堆的一些性質和結論github

最大堆、最小堆

  • 若是任意節點的值老是>=字節的的值,稱爲:最大堆、大根堆、 大頂堆
  • 若是任意節點的值老是<=字節的的值,稱爲:最小堆、小根堆、 小頂堆

索引i的規律(n是元素數量)

  • 若是i=0 它是根節點
  • 若是 2*i+1<=n-1 它的左子節點的索引爲2*i+1
  • 若是 2*i+1>n-1 它沒有左子節點
  • 若是 2*i+2<=n-1 它的右子節點的索引爲2*i+2
  • 若是 2*i+2>n-1 它沒有右子節點

算法穩定性

  • 堆排序不是一種穩定排序算法。

是不是原地算法

  • 何爲原地算法?
    • 不依賴額外的資源或者依賴少數的額外資源,僅依靠輸出來覆蓋輸入
    • 空間複雜度爲 𝑂(1) 的均可以認爲是原地算法
  • 非原地算法,稱爲 Not-in-place 或者 Out-of-place
  • 堆排序屬於 In-place

代碼

public class HeapSort <T extends Comparable<T>> extends Sort<T>{

	private int heapSize;
	@Override
	protected void sort() {
		// 原地建堆  自下而上的下濾
		heapSize = array.length;
		// heapSize>>1-1 第一個非葉子節點的下標
		for (int i = (heapSize>>1)-1; i >=0; i--) {
			siftDown(i);
		}
		
		while (heapSize>1) {
			// 交換堆頂元素和尾部元素
			swap(0, heapSize-1);
			//堆頂元素最大放到尾部了。下次就不須要考慮這個了。因此size要減1
			heapSize--;
			// 下濾 維護堆的性質
			siftDown(0);
		}
	}
	
	//讓index位置的元素下濾
	private void siftDown(int index) {
		// 取出要下濾的座標的值
		T element = array[index];
		int half = heapSize>>1;
		// 第一個葉子節點的索引 == 非葉子節點的數量
		// index<第一個葉子節點的索引
		// 必須保證index位置是非葉子節點
		while (index<half) {
			// index的節點有2種狀況
			// 1.只有左子節點
			// 2.同時有左右子節點
						
			// 默認爲左子節點跟它進行比較
			int childIndex = (index<<1)+1;
			T child = array[childIndex];
			// 右子節點
			int rightIndex = childIndex +1;
			// 選出左右子節點最大的那個
			if (rightIndex<heapSize && cmp(array[rightIndex], child)>0) {
				childIndex = rightIndex;
				child = array[childIndex];
			}
			
			if (cmp(element, child)>=0) {
				break;
			}
			// 將子節點存放到index位置
			array[index]= child;
			// 從新設置index
			index = childIndex;
		}
		// 將目標值存放到最終的index位置
		array[index] = element;
		
	}
}

複製代碼

驗證

數據源:從1到20000之間隨機生成10000個數據來測試算法

Integer[] array = Integers.random(10000, 1, 20000);bash

結果以下:數據結構

【HeapSort】 穩定性:false 耗時:0.008s(8ms) 比較次數:23.54萬 交換次數:9999dom

【BubbleSort】 穩定性:true 耗時:0.502s(502ms) 比較次數:4999.50萬 交換次數:2489.42萬ide

【SelectionSort】 穩定性:true 耗時:0.115s(115ms) 比較次數:4999.50萬 交換次數:9999性能

能夠看到堆排序明顯比選擇排序和冒泡排序的性能高不少。測試

代碼地址:

相關文章
相關標籤/搜索