引言
排序模塊會把所有的排序算法都整理一遍,方便自己以后回身學習。在本篇博文中主要介紹了時間復雜度為O(N^2)的插入排序算法,並用demo進行實現。不過在開始之前需要學習一些基礎的數學知識,希望對大家有所幫助。筆者目前整理的一些blog針對面試都是超高頻出現的。大家可以點擊鏈接:http://blog.csdn.net/u012403290
技術點
1、4個符號(O, Ω,Θ, o)
在本排序集合模塊中,這4個符號用於表示兩個函數之間的相對增長率,他們分別讀作O(大O), Ω(omega),Θ(theta),o(小O)。
2、4個定義
①如果存在正常數c和n使得當N>=n時,T(N)<=cf(N),則記為T(N)=O(f(N))。
解釋下什么意思,如果存在兩個函數T(N)和f(N)且兩個函數圖的線是相交的,那么存在一個點n,使得兩者的值相等。其中c是一個常數。比如說10N 和N^2這兩個函數,當且僅當N=10的時候兩者相交,如下圖:
在10這個點之前,T(N)的值比f(N)小,但是f(N)的相對增長率大,在10之后會一直比T(N)大,也就是n點之后f(N)的增長率比T(N)大, 寫為10N = O(N^2)。
②如果存在正常數c和n使得當N>=n時,T(N)>=cg(N),則記為T(N)=Ω(g(N))。
和上面①的定義是差不多的,只不過是相對增長率反過來了,n點之后g(N)的增長率比T(N)小。上面的例子在這里就變成了T(N)= N^2,g(N)=10N了,也就是N^2 = Ω(10N)。
③當T(N)=O(h(N))且T(N) = Ω(h(n))時,可以用T(N) = Θ(h(N))。
現在很好理解了,意思就是兩個函數的增長率是相同的,那么就可以用Θ表示。
④如果存在正常數c和n使得當N>n時,T(N)< cp(N),則記為T(N)=o(p(N))。
乍眼一看是不是感覺和第①條很像?其實兩者最大的區別就是兩個函數圖有沒有增長率相等的情況,讀作p(N)的增長率比T(N)大,比如說下面這張圖:
現學現用的話那么就是T(N) = O(p(N))且T(N)!=Θ(p(N))
這些定義在描述算法的效率問題上會有非常大的作用。
3、上界與下界
簡單來說,在T(N) = O(f(N))中,f(N)就是T(N)的一個上界;同理,在T(N)上面的等式也可以表示成f(N) = Ω(T(N)),所以T(N)就是f(N)的一個下界。
插入排序
比較專業來說,就是:插入排序對於N個元素的排序需要進行N-1趟處理,p表示當前處理元素位置(或者是處理趟數),p=1到N-1趟中的任何一步都保證從0到p位置的順序是排好的。
通俗來說就是對於一個要排序的數據,將數據分為已排序和未排序兩部分,按順序將一個未排序的數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據,未排序的數據減一,直到數據全部是排序的。下面這張圖描述了過程:
第一趟:處理8, 34是屬於已排好序的(一個值不用排序),8<34,那么繼續往前遍歷,發現前面沒數據了,那么就把8插入到34前面。
第二趟:處理64,64>34(從后面往前面比較),因為64比已排序好的最大的還要大,所以直接插入到34的后面。
第三趟:處理51,51<64,那么繼續遍歷,接着51>34,因為51比前面排好序最大的要大,所以停止遍歷,把51插入到34的后面
第四趟:處理32,32<64,那么繼續遍歷,32<51,那么還是繼續遍歷,32<34,那么還是繼續遍歷。32>8,遍歷結束,把32插入到8后面。
第五趟:處理21,21<64,那么繼續遍歷,21<51,那么繼續遍歷,21<34,那么繼續遍歷,21<32,那么繼續遍歷。21>8,遍歷結束,把21插入到8后面。
排序結束。
代碼實現
以下就是我實現的插入排序,排序對象是int型的數組:package com.brickworkers;
/**
*
*@author Brickworker
* Date:2017年4月19日下午3:25:27
* 關於類SortTest.java的描述:插入排序
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class SortTest{
public static void sort(int[] t) {
int j;
for (int p = 1; p < t.length; p++) {// 遍歷數組,從1下標開始,0位置一個元素不用排序
// 用temp暫存要排序的值
int temp = t[p];
// 代處理的數據,在已排好序的數組中從后往前進行比較遍歷
// 條件是:代處理的數據是否比有序的數據中某一個值小,如果小那么就需要對數組進行移位
for (j = p; j > 0 && temp < t[j - 1]; j--) {
// 數組移位,為數據的插入做准備
t[j] = t[j - 1];
}
// 把數據插入到合適的j位置
t[j] = temp;
}
}
public static void main(String[] args) {
int[] ints = { 1, 5, 4, 2, 6, 4, 6, 47, 5, 4, 12, 41, 66, 4, 2, 10, 42 };
sort(ints);
for (int i : ints) {
System.out.print(i + " ");
}
}
}
//輸出:1 2 2 4 4 4 4 5 5 6 6 10 12 41 42 47 66
我們對上面的算法進行分析,由於兩個for循環的嵌套,所以插入排序的時間復雜度為O(N^2)。如果是處理已排好序的數據來說,那么插入排序就是O(N),因為第二個循環在判斷的時候就結束了。考慮最差情況就是待處理數據是有序但是和預期相反,那么N個元素的執行次數就是:
1+2+….+(N-1) = (1+N-1)*N/2 =O(N^2)。
尾記
編程,算法是必須的;算法,數學是必須的。我有個朋友為了寫好代碼,學過數學(線代,離散),學過經濟,學過畫畫,學過文學。所以,不要覺得自己學這個沒用,看那個沒用,其實只是你還暫時沒有用到罷了。