十大排序算法之插入排序

本文首發於我的博客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;
	}

複製代碼

什麼是插入排序

  • 插入排序(Insertion sort)是一種簡單直觀且穩定的排序算法。若是有一個已經有序的數據序列,要求在這個已經排好的數據序列中插入一個數,但要求插入後此數據序列仍然有序,這個時候就要用到一種新的排序方法——插入排序法,插入排序的基本操做就是將一個數據插入到已經排好序的有序數據中,從而獲得一個新的、個數加一的有序數據,算法適用於少許數據的排序,時間複雜度爲O(n^2)。是穩定的排序方法。插入算法把要排序的數組分紅兩部分:第一部分包含了這個數組的全部元素,但將最後一個元素除外(讓數組多一個空間纔有插入的位置),而第二部分就只包含這一個元素(即待插入元素)。在第一部分排序完成後,再將這個最後元素插入到已排好序的第一部分中。。

插入排序的基本思想

每步將一個待排序的記錄,按其關鍵碼值的大小插入前面已經排序的文件中適當位置上,直到所有插入完爲止。github

算法穩定性

  • 插入排序是一種穩定排序算法。

是不是原地算法

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

時空複雜度

  • 最好時間複雜度:O(n)
  • 最壞、平均時間複雜度:O(n^2)
  • 空間複雜度:O(1)

代碼

思路:第一種方案是每次比較。若是後面的比前面的小。就交換位置。算法

package YZ.Sort;

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

	@Override
	protected void sort() {
		// TODO Auto-generated method stub
		for (int begin = 1; begin < array.length; begin++) {
			int cur = begin;
			while (cur>0 && cmp(array[cur],array[cur-1])<0) {
				swap(cur, cur-1);
				cur--;
			}
		}
	}
}
複製代碼

優化

思路:將 交換 改成 挪動數組

  • 先將待插入的元素備份
  • 頭部有序數據中比待插入元素大的,都朝尾部方向挪動一個位置
  • 將待插入元素放在最終的合適位置
public class InsertionSort2<T extends Comparable<T>> extends Sort<T>  {

	@Override
	protected void sort() {
		for (int begin = 1; begin < array.length; begin++) {
			int cur = begin;
			T res =array[cur];
			while (cur>0 && cmp(res,array[cur-1])<0) {
				array[cur] = array[cur-1];
				cur--;
			}
			array[cur] = res;
		}
		
	}

}

複製代碼

二分法優化

前面的代碼中,想要插入一個元素,是經過逐個比較來尋找要插入的位置。其實能夠經過二分法來更快速的查找位置。bash

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

	@Override
	protected void sort() {
		// TODO Auto-generated method stub
		for (int begin = 1; begin < array.length; begin++) {
			insert(begin, searchIndex(begin));
		}
	}

	/**
	 * 將source位置的元素插入到dest位置
	 * @param source
	 * @param dest
	 */
	private void insert(int source,int dest) {
		T v = array[source];
		for (int i = source; i > dest; i--) {
			array[i] = array[i-1];
		}
		array[dest] = v;
	}
	
	/**
	 * 利用二分搜索找到 index 位置元素的待插入位置
	 * 已經排好序數組的區間範圍是 [0, index)
	 * @param index
	 * @return
	 */
	private int searchIndex(int index) {
		int begin = 0;
		int end = index;
		while (begin<end) {
			int mid = (begin+end)>>1;
			if (cmp(array[index], array[mid])<0) {
				end = mid;
			}else {
				begin = mid +1;
			}
			
		}
		return begin;
	}

}

複製代碼

結果

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

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

結果以下:post

【BubbleSort】 穩定性:true 耗時:0.481s(481ms) 比較次數:4999.50萬 交換次數:2467.42萬性能

【BubbleSort1】 穩定性:true 耗時:0.428s(428ms) 比較次數:4998.82萬 交換次數:2467.42萬

【BubbleSort2】 穩定性:true 耗時:0.405s(405ms) 比較次數:4993.60萬 交換次數:2467.42萬

【InsertionSort1】 穩定性:true 耗時:0.239s(239ms) 比較次數:2468.42萬 交換次數:2467.42萬

【InsertionSort2】 穩定性:true 耗時:0.186s(186ms) 比較次數:2468.42萬 交換次數:0

【InsertionSort3】 穩定性:true 耗時:0.114s(114ms) 比較次數:11.90萬 交換次數:0

【HeapSort】 穩定性:false 耗時:0.005s(5ms) 比較次數:23.53萬 交換次數:9999

能夠看到插入排序的性能高於冒泡排序,可是低於堆排序

代碼地址:

相關文章
相關標籤/搜索