1. 程式人生 > >插入排序的實現與分析

插入排序的實現與分析

讓你對一副雜亂無序的撲克牌進行排序,最常用的方法就是插入排序了。

插入排序的原理很簡單,在雜亂的牌中選出一張牌,然後把它插入到應有的位置,假設是從左到右依次遞減的。以鬥地主的規則為例,現在你手上的牌有{3, 2, J, K, A},那麼“2”就應該插在“3”前面,變成{2, 3, J, K, A},然後{2, J, 3, K, A} {2, K, J, 3, A} {2, A, K, J, 3}。

看個例子:

*************************
插入前:1 7 5 4 6 -3 8 
待插入元素a[0]=1, 插入位置:0
插入後:1 7 5 4 6 -3 8 
*************************
插入前:1 7 5 4 6 -3 8 
待插入元素a[1]=7, 插入位置:1
插入後:1 7 5 4 6 -3 8 
*************************
插入前:1 7 5 4 6 -3 8 
待插入元素a[2]=5, 插入位置:1
插入後:1 5 7 4 6 -3 8 
*************************
插入前:1 5 7 4 6 -3 8 
待插入元素a[3]=4, 插入位置:1
插入後:1 4 5 7 6 -3 8 
*************************
插入前:1 4 5 7 6 -3 8 
待插入元素a[4]=6, 插入位置:3
插入後:1 4 5 6 7 -3 8 
*************************
插入前:1 4 5 6 7 -3 8 
待插入元素a[5]=-3, 插入位置:0
插入後:-3 1 4 5 6 7 8 
*************************
插入前:-3 1 4 5 6 7 8 
待插入元素a[6]=8, 插入位置:6
插入後:-3 1 4 5 6 7 8 
*************************

附上程式碼:

public class insert_sort {
	public static void sort(int[] a){
		int n=a.length;
		for (int i=0; i<n; i++){			
			for (int j=i; j>0&&(a[j-1]>a[j]); j--)
				swap(a, j-1, j);//for 2
		}//for 1
	}
	public static void swap(int a[], int i, int j){
		int temp=a[i];
		a[i]=a[j];
		a[j]=temp;
	}
	public static void main(String[] args){
		int[] a={1, 7, 5, 4, 6, -3, 8};
		insert_sort.sort(a);
	}
}

演算法分析:

假設陣列的長度為n,每次插入操作都要移動一些元素的位置,如果要把最後一個元素恰好最小,那麼就需要移動n-1個元素。這是最壞的情況,最好的情況依然是陣列一開始就是有序排列的。對於隨機排列的長度為N且不重複的陣列,平均情況下插入排序要~(N^2)/4次比較以及~(N^2)/4次交換。最壞情況下需要~(N^2)/次比較和~(N^2)/2次交換,最好情況下需要N-1次比較和0次交換。總的來說,插入排序的時間複雜度為O(N^2)。

驗證:

//C為比較次數,S為交換次數
public static void sort(int[] a){
		int n=a.length;
		int C=0, S=0;
		int i, j, k;
		for (i=0; i<n; i++){			
			for (j=i; j>0&&(a[j-1]>a[j]); j--, C++, S++)
				swap(a, j-1, j);//for 2
		}//for 1
		System.out.println("C="+C+", S="+S);
	}
********************************************
假設N=100
最壞情況:
public static void main(String[] args){
		int []a=new int[100];
		for (int i=99; i>=0; i--)
			a[99-i]=i;
	    insert_sort.sort(a);
	}
結果:
C=4950, S=4950, (N^2)/2=5000
********************************************
最好情況:
public static void main(String[] args){
		int []a=new int[100];
		for (int i=0; i<100; i++)
			a[i]=i;
		insert_sort.sort(a);
	}
結果:
C=0(實際上比較了N-1次,因為沒有動作所以沒有記錄), S=0
********************************************
平均情況:
public static void main(String[] args){
		int []a=new int[100];
		for (int i=0; i<100; i++)
			if (i%2==0)
				a[i]=i;
			else
				a[i]=-i;
		insert_sort.sort(a);
	}
結果:
C=2500, S=2500, (N^2)/4=2500
********************************************

通過測試可以看到,程式與理論吻合得相當好。還可以得出一個結論:插入排序需要的交換次數和陣列中倒置的數量相同,因為每次交換都改變了一組倒置的元素,當倒置的元素數量為0時就完成了排序。

實際上插入排序的效能優於選擇排序,但它和氣泡排序一樣,需要大量移動元素。你可以嘗試用連結串列來完成插入排序,那樣會免去移動元素的操作,但同時會付出更多空間的代價。

BY DXH924

2018.10.19