java實現排序(1)-插入排序

引言

排序模塊會把所有的排序算法都整理一遍,方便自己以後回身學習。在本篇博文中主要介紹了時間複雜度爲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)。

尾記

編程,算法是必須的;算法,數學是必須的。我有個朋友爲了寫好代碼,學過數學(線代,離散),學過經濟,學過畫畫,學過文學。所以,不要覺得自己學這個沒用,看那個沒用,其實只是你還暫時沒有用到罷了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章