數據結構與算法學習筆記之如何分析一個排序算法?
前言
現在IT這塊找工作,不會幾個算法都不好意思出門,排序算法恰巧是其中最簡單的,我接觸的第一個算法就是它,但是你知道怎麽分析一個排序算法麽?有很多時間復雜度相同的排序算法,在實際編碼中,那又如何選擇呢?下面我們帶著問題一起學習一下。
正文
一、常見經典的排序方法
(圖片來自於一像素)
插入排序
希爾排序(遞減增量排序算法)
歸並排序
快速排序
冒泡排序
選擇排序
計數排序
計數排序
堆排序
二、 按照時間復雜度歸類
時間復雜度O(n2): 冒泡排序、插入排序、選擇排序 時間復雜度O(nlogn):快速排序、歸並排序 時間復雜度O(n): 計數排序、基數排序、桶排序
三、如何分析一個“排序算法”?
從三個方面入手
a、算法的執行效率
1.最好、最壞、平均情況時間復雜度。 從算法的核心,復雜度入手,給出最好最壞,平均情況下的時間復雜度,便於分析2. 時間復雜度的系數、常數和低階。 時間復雜度表示的是規模很大的一種增漲趨勢,很容易就忽略系數,低階,常數等,實際開發中排序的規模都是像10.100.1000這種小規模
3. 比較次數,交換(或移動)次數。 排序算法執行過程中,涉及兩種操作,一種是元素比較大小,一種是元素交換或移動位置,所以比較次數,交換次數都得考慮進去。
b、排序算法的內存消耗
算法消耗可以通過空間復雜度來衡量
原地排序算法:特指空間復雜度是O(1)的排序算法。
c、排序算法的穩定性
1. 穩定性概念:如果待排序的序列中存在值相等的元素,經過排序之後,相等元素之間原有的先後順序不變。2. 穩定性重要性:可針對對象的多種屬性進行有優先級的排序。
3. 舉例:給電商交易系統中的“訂單”排序,按照金額大小對訂單數據排序,對於相同金額的訂單以下單時間早晚排序。用穩定排序算法可簡潔地解決。先按照下單時間給訂單排序,排序完成後用穩定排序算法按照訂單金額重新排序。
四、詳解冒泡排序
冒泡排序只會操作相鄰的兩個數據。每次冒泡操作都會對相鄰的兩個元素進行比較,看是否滿足大小關系要求,如果不滿足就讓它倆互換。默認從小到大未有序有序度:數組中具有有序關系的元素對的個數。 滿有序度:完全有序的數組 逆序度:數組中具有無序關系的元素對的個數。
逆序度=滿有序度-有序度 排序的過程實際上就是增加有序度,減少逆序度的過程時間復雜度:
1. 最好情況(滿有序度):O(n)。
2. 最壞情況(滿逆序度):O(n^2)。
3. 平均情況:
“有序度”和“逆序度”:對於一個不完全有序的數組,如4,5,6,3,2,1,有序元素對為3個(4,5),(4,6),(5,6),有序度為3,逆序度為12;對於一個完全有序的數組,如1,2,3,4,5,6,有序度就是n*(n-1)/2,也就是15,稱作滿有序度;逆序度=滿有序度-有序度;冒泡排序、插入排序交換(或移動)次數=逆序度。
最好情況下初始有序度為n*(n-1)/2,最壞情況下初始有序度為0,則平均初始有序度為n*(n-1)/4,即交換次數為n*(n-1)/4,因交換次數<比較次數<最壞情況時間復雜度,所以平均時間復雜度為O(n2)。 代碼實現:
// 冒泡排序,a 表示數組,n 表示數組大小
public void bubbleSort(int[] a, int n) {
if (n <= 1) return;
for (int i = 0; i < n; ++i) {
// 提前退出冒泡循環的標誌位
boolean flag = false;
for (int j = 0; j < n - i - 1; ++j) {
if (a[j] > a[j+1]) { // 交換
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
flag = true; // 表示有數據交換
}
}
if (!flag) break; // 沒有數據交換,提前退出
}
}
五、詳解插入排序
將數據分為兩個區間,已排序區間和未排序區間,初始已排序區間只有一個元素(即第一個數據),我們取未排序區間的元素,在已排序的區間中找到合適的位置插入位置插入,並保證已排序區間數據一直有序,重復過程,直到未排序區間中沒有元素
運行過程中看得出來,不需要額外的存儲空間,所以空間復雜度為0(1),也是原地排序算法
同樣值的元素,前後順序保持不變,是穩定的排序算法
時間復雜度:
最好時間復雜度為O(n)
最壞時間復雜度為O(n2)
平均時間復雜度為O(n2)
代碼實現:
// 插入排序,a 表示數組,n 表示數組大小
public void insertionSort(int[] a, int n) {
if (n <= 1) return;
for (int i = 1; i < n; ++i) {
int value = a[i];
int j = i - 1;
// 查找插入的位置
for (; j >= 0; --j) {
if (a[j] > value) {
a[j+1] = a[j]; // 數據移動
} else {
break;
}
}
a[j+1] = value; // 插入數據
}
}
六、詳解選擇排序
選擇排序將數組數據分成已排序區間和未排序區間。初始已排序區間只有一個元素,即數組第一個元素。在未排序區間找到最小的數據,將其放在已排序區間的末尾空間復雜度為O(1),選擇排序是原地排序算法。 未排序區間的元素和已排序區間的元素相同時,它可以放在已排序區間相同值的前或後,所以為不穩定的排序
時間復雜度:
1. 最好情況:O(n2)。
2. 最壞情況:O(n2)。
3. 平均情況:O(n2)(往數組中插入一個數的平均時間復雜度是O(n),一共重復n次)。
七、各種排序方法的匯總比較
八、選擇排序和插入排序的時間復雜度相同,都是O(n^2),在實際的軟件開發中,為什麽我們更傾向於使用插入排序而不是冒泡排序算法呢?
答:它們的元素比較次數以及交換元素的次數都是原始數據的逆序度,是一個固定值,但是從代碼實現上來看,冒泡排序的數據交換要比插入排序的數據移動要復雜,冒泡排序需要3個賦值操作,而插入排序只需要1個,他們 的時間復雜度上都是O(n2),但是為了追求極致的性能,所以首選插入排序算法結尾
大家不妨試著分析一下其他的幾種算法。
看再多遍都不如寫一篇來得深刻,建議大家多敲。
相關文章
數據結構與算法學習筆記之寫鏈表代碼的正確姿勢(下)
數據結構與算法學習筆記之 提高讀取性能的鏈表(上)
數據結構與算法學習筆記之 從0編號的數組
數據結構與算法學習筆記之後進先出的“桶”
數據結構與算法學習筆記之先進先出的隊列
數據結構與算法學習筆記之高效、簡潔的編碼技巧“遞歸”
以上內容為個人的學習筆記,僅作為學習交流之用。
歡迎大家關註公眾號,不定時幹貨,只做有價值的輸出
作者:Dawnzhang
出處:https://www.cnblogs.com/clwydjgs/p/9815690.html
版權:本文版權歸作者
轉載:歡迎轉載,但未經作者同意,必須保留此段聲明;必須在文章中給出原文連接;否則必究法律責任
數據結構與算法學習筆記之如何分析一個排序算法?