1 如何評價、分析一個排序算法?算法
不少語言、數據庫都已經封裝了關於排序算法的實現代碼。因此咱們學習排序算法目的更多的不是爲了去實現這些代碼,而是靈活的應用這些算法和解決更爲複雜的問題,因此更重要的是學會如何評價、分析一個排序算法並在合適的場景下正確使用。數據庫
分析一個排序算法,主要從如下3個方面入手:學習
1.1 排序算法的執行效率測試
1)最好狀況、最壞狀況和平均狀況時間複雜度spa
待排序數據的有序度對排序算法的執行效率有很大影響,因此分析時要區分這三種時間複雜度。除了時間複雜度分析,還要知道最好、最壞狀況複雜度對應的要排序的原始數據是什麼樣的。code
2)時間複雜度的係數、常數和低階blog
時間複雜度反映的是算法執行時間隨數據規模變大的一個增加趨勢,平時分析時每每忽略係數、常數和低階。但若是咱們排序的數據規模很小,在對同一階時間複雜度的排序算法比較時,就要把它們考慮進來。排序
3)比較次數和交換(移動)次數內存
內排序算法中,主要進行比較和交換(移動)兩項操做,因此高效的內排序算法應該具備儘量少的比較次數和交換次數。博客
1.2 排序算法的內存消耗
也就是分析算法的空間複雜度。這裏還有一個概念—原地排序,指的是空間複雜度爲O(1)的排序算法。
1.3 穩定性
若是待排序的序列中存在值相等的元素,通過排序以後,相等元素之間原有的前後順序不變,那麼這種排序算法叫作穩定的排序算法;若是先後順序發生變化,那麼對應的排序算法就是不穩定的排序算法。
在實際的排序應用中,每每不是對單一關鍵值進行排序,而是要求排序結果對全部的關鍵值都有序。因此,穩定的排序算法每每適用場景更廣。
2 三種時間複雜度爲O(n2)的排序算法
2.1 冒泡排序
2.1.1 原理
兩兩比較相鄰元素是否有序,若是逆序則交換兩個元素,直到沒有逆序的數據元素爲止。每次冒泡都會至少讓一個元素移動到它應該在的位置。
2.1.2 實現
void BubbleSort(int *pData, int n) //冒泡排序 { int temp = 0; bool orderlyFlag = false; //序列是否有序標誌 for (int i = 0; i < n && !orderlyFlag; ++i) //執行n次冒泡 { orderlyFlag = true; for (int j = 0; j < n - 1 - i; ++j) //注意循環終止條件 { if (pData[j] > pData[j + 1]) //逆序 { orderlyFlag = false; temp = pData[j]; pData[j] = pData[j + 1]; pData[j + 1] = temp; } } } }
測試結果
2.1.3 算法分析
1)時間複雜度
最好狀況時間複雜度:當待排序列已有序時,只需一次冒泡便可。時間複雜度爲O(n);
最壞狀況時間複雜度:當待排序列徹底逆序時,須要n次冒泡。時間複雜度爲O(n2);
平均狀況時間複雜度:當待排序列徹底逆序時,逆序度爲n * (n - 1) / 2。只有當交換逆序對時纔會纔會使得有序,取中間逆序度n * (n - 1) / 4,那麼就要進行n * (n - 1) /4次交換,而比較的次數大於交換次數,因此平均狀況時間複雜度爲O(n2)。
2)空間複雜度
只借助了一個臨時變量temp,因此空間複雜度爲O(1)。
3)穩定性
該算法中只有交換操做會改變數據元素的順序,只要咱們在數據元素值相等時不交換數據元素,那麼算法就是穩定的。
4)比較和交換的次數
交換操做的執行次數與逆序度相等,比較操做的執行次數大於等於逆序度小於等於n * (n - 1) / 2。
2.2 插入排序
2.2.1 原理
將待排序序列分爲已排序區間和未排序區間,開始時已排序區間只有一個數據元素也就是序列的第一個元素,將未排序區間中的數據元素插入已排序區間中同時保持已排序區間的有序,直到未排序區間沒有數據元素。
2.2.2 實現
void InsertSort(int *pData, int n) //插入排序 { int temp = 0, i, j; for (i = 1; i < n; ++i) //未排序區間 { if (pData[i] < pData[i - 1]) //逆序 { temp = pData[i]; for (j = i - 1; pData[j] > temp; --j) //搬移數據元素 pData[j + 1] = pData[j]; pData[j + 1] = temp; //插入數據 } } }
測試結果:
2.2.3 算法分析
1)時間複雜度
最好狀況時間複雜度:當待排序列已有序時,只需遍歷一次便可完成排序。時間複雜度爲O(n);
最壞狀況時間複雜度:當待排序列徹底逆序時,須要進行n-1次數據搬移和插入操做。時間複雜度爲O(n2);
平均狀況時間複雜度:與冒泡法的分析過程同樣,平均狀況時間複雜度爲O(n2)。
2)空間複雜度
排序過程當中只須要一個臨時變量存儲待插入數據,空間複雜度爲O(1)。
3)穩定性
插入排序過程當中只有插入操做會改變數據元素的相對位置,只要元素大小比較時相等狀況下不進行插入操做,插入排序算法就是穩定的。
4)比較操做和數據搬移操做執行次數
數據搬移操做執行次數和逆序度相同。比較操做次數大於等於逆序度,小於等於n * (n - 1) / 2。
2.3 選擇排序
2.3.1 原理
選擇排序的原理相似於插入排序都分爲已排序區間和未排序區間,選擇排序的已排序區間初始大小爲零,每次從未排序區間取關鍵值最大(或最小)的數據元素放在已排序區間的後一個位置,直到未排序區間沒有數據元素則完成排序。
2.3.2 實現
void SelectSort(int *pData, int n) //選擇排序 { int i, j, min, temp; for (i = 0; i < n; ++i) //未排序區間 { min = i; //最小值下標 for (j = i + 1; j < n; ++j) { if (pData[min] > pData[j]) //逆序 min = j; //保存當前較小值下標 } if (i != min) //若是不是最小值,交換元素 { temp = pData[i]; pData[i] = pData[min]; pData[min] = temp; } } }
測試結果:
2.3.3 算法分析
1)時間複雜度
無論是已有序序列仍是徹底逆序序列,都要進行n次遍歷無序區間操做,時間複雜度爲O(n2)。
2)空間複雜度
排序過程當中只須要保存每次遍歷無序區間最小值的下標和第i個元素的數值,因此空間複雜度爲O(1)。
3)穩定性
選擇排序算法中改變數據元素相對位置的操做爲交換操做,當第i次中第i個數據元素不爲當前無序區間最小值時則和最小值交換數據元素。當有重複元素時,就有可能發生相對位置改變。例如5,3,4,5,1第一次選擇操做後爲1,3,4,5,5,此時兩個5的相對位置已經改變。因此選擇排序算法不是穩定的。
4)比較操做和交換操做的執行次數
比較操做執行次數爲n * (n - 1) / 2,交換操做執行次數小於等於n-1。
2.4 三種算法之間的比較
1)通常待排序列長度n較小時,咱們選擇這三種排序算法;
2)當排序要求穩定時,通常選擇插入排序,由於相同的狀況下,移動數據比交換數據執行速度快;
3)當數據元素信息量較大時,能夠考慮用選擇排序,由於它交換操做執行次數最少。
該篇博客是本身的學習博客,水平有限,若是有哪裏理解不對的地方,但願你們能夠指正!