内部排序算法全面总结
排序的概念
排序,就是重新排列表中的元素,使表中的元素按照關(guān)鍵字有序的過程。
我所了解的 表 多半是順序表,因為順序表實現(xiàn)較為簡單,但是鏈表結(jié)構(gòu)同樣可以實現(xiàn)很多排序算法。
定義中的元素一般指什么元素呢?
一般可分為兩大類 基本類型 自定義類的類型
基本類型指的就是整型,浮點型,字符型這些屬于語言基本類型的東西,比如在js中基本類型就包括 string number bool object symbol,undefined,null
自定義類一般就是 語言中封裝的結(jié)構(gòu)體或者類,對象等,不同編程語言可能有些差別,C語言中就是struct結(jié)構(gòu)體 js中就是包含相同子段的對象的集合。
上圖展示C語言中的結(jié)構(gòu)體,一個Student結(jié)構(gòu)體中包括了name,
age,score三個字段。
我們?yōu)槭裁葱枰獙ε判虻脑刈鲞@樣的劃分呢?
排序的穩(wěn)定性
回答上面的問題牽涉到排序算法的穩(wěn)定性。首先給出排序算法穩(wěn)定性的一個拗口的定義:
假設(shè)待排序表中有兩個元素Ri和Rj,其對應(yīng)的關(guān)鍵字相同即keyi = keyj,且在排序前Ri在Rj之前,若使用某一算法排序后,Ri仍在Rj之前,則稱這個排序算法是穩(wěn)定的,反之則不穩(wěn)定。
注意,此時的排序算法是針對關(guān)鍵字key的。那么這段定義到底表述的是什么意思呢?我們可以先把目光集中到上面定義的Student結(jié)構(gòu)體中。
假設(shè)我們現(xiàn)在有個關(guān)于Student的順序表,對于關(guān)鍵字age來說,順序表中的結(jié)構(gòu)體是無序的,因為我們期待的是age從小到大排列,實際情況卻是從大到小。
那么如果一個排序算法要針對上述age字段進(jìn)行排序,我們可以發(fā)現(xiàn)的一點是張三和李四的年齡是一樣的,并且張三在李四的前面,排序算法按照上述定義如果是穩(wěn)定的那么排完序之后,王二到了第一位,因為他年齡最小,并且張三和李四的前后相對位置不能發(fā)生改變。
排序的穩(wěn)定性有何意義?
它可以記錄數(shù)據(jù)排列之前的狀態(tài),舉一個實際的例子,假設(shè)我們一張表,表中記錄了一個學(xué)校全部學(xué)生的高考成績,字段包括姓名,班級,分?jǐn)?shù)。
一開始表中數(shù)據(jù)狀態(tài)是散亂的,首先我們按照成績進(jìn)行從大到小排序,我們會得到一張新表,表中的記錄是按照成績的高低從大到小依次展示的。 然后我們再按照班級來進(jìn)行排序,那么結(jié)果會是怎么樣的呢?
如果我們使用的排序算法是穩(wěn)定的話,表中的記錄會是這樣的:一班同學(xué)全體在表的開始,然后是二班,三班。。。
并且每班同學(xué)的成績是從高到低依次排列的
現(xiàn)在大家應(yīng)該可以理解排序算法穩(wěn)定性的重要性意義了。
我們回到最開始的問題,排序元素為什么要分為基礎(chǔ)數(shù)據(jù)和自定義類呢?
要知道基礎(chǔ)數(shù)據(jù)是長得一模一樣的,那么排序的穩(wěn)定性就不需要關(guān)注了,可是自定義類就不一樣了,就向上面所舉的例子,因為一條記錄包含不同的字段,實際過程中不可能有兩條一模一樣的記錄。
排序的分類
內(nèi)部排序
排序期間元素全部放在內(nèi)存中的排序
外部排序
指排序期間元素?zé)o法全部同時存放在內(nèi)存中,必須在排序的過程中根據(jù)要求不斷在內(nèi)外存之間移動的排序。
主要內(nèi)容
我們研究的排序算法主要是內(nèi)部排序,內(nèi)部排序包括 插入排序,交換排序,選擇排序,歸并排序,基數(shù)排序等。下面分塊總結(jié)
排序總結(jié)
插入排序
插入排序包括三種排序方法 直接插入排序,折半插入排序, 希爾排序。
直接插入排序
思路:
(1)假設(shè)我們有一個序列List,它的前一小部分已經(jīng)是有序的了,后面的部分還是無序的。
我們從無序的部分中拿出第一個元素 A ,將他與前面的有序部分的最后一個元素進(jìn)行 S 比較,如果A比S大,那么不需要改變A的位置,插入結(jié)束。如果 A 比 S小,那么就需要將A與S交換位置,然后一直比較下去,直到找到第一個小于等于他的數(shù)字為止,交換結(jié)束,插入完畢。
(2)第二種思路,找到L(i)在L[0…n]中的插入位置k,將L[k…i-1]中的所有元素后移一個位置,將L(i)復(fù)制到前面已經(jīng)排好的序列中去。
代碼解釋:首先我們定義了一個swap的方法,這個方法就是為了交換一個數(shù)組中指定位置的兩個數(shù)。內(nèi)部排序算法多半就基于比較和交換的,所以我們在其他的排序算法中同樣會使用swap方法,到時候就不再贅述。
上面的直接排序算法是根據(jù)思路1寫出,思路2的代碼本人認(rèn)為不夠簡潔以及容易理解,在此不再贅述。
空間復(fù)雜度:O(1)
時間復(fù)雜度:O(n2)
排序穩(wěn)定性:為穩(wěn)定的排序算法,因為只有插入項小于比較項的時候才會發(fā)生交換。
適用性:適用于順序表和鏈表。
折半插入排序
折半插入排序就是在尋找插入位置的時候不是一個個向前比較,而是利用二分法迅速的找到我們需要插入的位置,但是我們需要向后移動元素的個數(shù)并沒有減少,所以折半插入排序的時間復(fù)雜度依然是O(n2)
總之折半直接插入算法,就是直接插入算法的基礎(chǔ)上加入了二分查找法。
希爾排序
直接插入算法適用于基本有序的排序表和數(shù)據(jù)量不大的排序表。也就是說數(shù)組越有序,那么希爾排序的效率越高,因為他所需要移動的元素越少。
又有一位比較牛逼的科學(xué)家 Donald Shell提出了一個觀點,要是可以把待排序表分割成若干組長度較小的序表,然后對各個子表進(jìn)行直接插入排序,當(dāng)整個表中的元素已呈基本有序,再對全體記錄進(jìn)行一次直接插入排序。
通俗點說我們 可以理解為表中各個小的地方都有序了,那么整體自然更加的趨向有序。
那么具體操作過程是什么?我們怎么去選擇子序列?
希爾提出,假設(shè)我們的數(shù)組長度是n 我們第一次取d1 = [n / 2]
所有距離為d1倍數(shù)的元素被分在了一組。
如果我們的數(shù)組長度為10,{0,1,2,4,5,6,7,8,9},那么第一次取d1 = 5,則數(shù)組中的分組為
【0 5】 【1 6】 【2 7】 【3 8】 【4 9】
我們先在這些個子序列中利用直接插入排序把他弄有序,然后縮短步長,取d2 = [d1 / 2] = 3(注意這里是向上取整)
那么此時的分組就是
【0 3 6 9】 【1 4 7】 【2 5 8】
注意d等于幾我們就分成了幾組
最后一組d一定是1,因為我們需要對整個數(shù)組進(jìn)行插入排序
代碼解析:希爾排序的代碼并不是那么好理解,首先我們?nèi)〔介L
d為 length / 2 說明我們要分d組 ,所以第一層for循環(huán)表示的是,每次循環(huán)表示一個分組。內(nèi)層循環(huán)表示遍歷這個組的所有數(shù),一個某個數(shù)滿足插入的條件進(jìn)行交換。循環(huán)結(jié)束后改變步長為 d / 2;d最小為1,比1小就跳出循環(huán)。
穩(wěn)定性:希爾排序不是穩(wěn)定的排序算法
插入排序的一些注意點
- 對n個不同的數(shù)據(jù)元素進(jìn)行直接插入排序,最多需要的比較次數(shù)為 n(n - 1)/ 2,最小的比較次數(shù)為 n - 1
- 待排序的元素序列基本有序的前提下,直接插入排序的時間復(fù)雜度接近O(n),且空間復(fù)雜度極低
- 折半插入排序和直接插入排序的區(qū)別僅僅在于元素之間的比較次數(shù)
交換排序
概念
王道教材上交換排序是指序列中兩個元素關(guān)鍵字的比較結(jié)果來對換這兩個記錄在序列中的位置。主要排序算法是冒泡排序和快速排序。其實我覺得這個分類并不夠準(zhǔn)確,因為插入排序也可以實現(xiàn)為交換排序,就跟我上面思路一實現(xiàn)的代碼一模一樣。
冒泡排序
冒泡排序很簡單,但是實際做項目里面也很少會用得到。
思路:從后往前(或者從前往后)兩兩比較相鄰元素的值,若為逆序則交換他們,直到序列比較完。第一趟冒泡的結(jié)果總是將最小的元素交換到待排序列的第一個位置(或者是把最大的元素交換到待排序列的最后一個位置)。
代碼實現(xiàn):
1.我見過最簡單的一種冒泡,對于長度為n的序列來說,需要經(jīng)過n -1次冒泡,每次固定住一個元素的位置。
這種算法有個明顯的缺陷,那就是比較次數(shù),就算是序列本身已經(jīng)是有序的了,還是需要比較,這就說明比較次數(shù)是和數(shù)據(jù)狀態(tài)無關(guān)的。
2.第二種代碼對上面代碼進(jìn)行了改進(jìn),如果在某躺冒泡中,我們發(fā)現(xiàn)元素并沒有進(jìn)行交換了,那么說明此時序列已經(jīng)排完了,可以跳出循環(huán)。
一個簡單的實驗,用一個count變量記錄第一種冒泡和第二種冒泡對于一個正序序列(長度為7)進(jìn)行比較的次數(shù),我們發(fā)現(xiàn)第一種count等于21,第二種為6.
時間復(fù)雜度:最好情況O(n),最壞情況O(n2)
空間復(fù)雜度:O(1)
比較次數(shù):最好情況 n -1,最壞情況 n(n -1)/2
穩(wěn)定性:穩(wěn)定的排序算法。
快速排序
快速排序是所有內(nèi)部排序算法中性能最優(yōu)的排序算法,并且也是最不容易理解的一種排序算法。
思路:
快排是我們接觸的第一種基于分治法的算法,他將一個大問題劃分成了兩個規(guī)模更小的小問題。我們首先從排序表中找到一個基準(zhǔn)元素(pivot)通過一趟排序?qū)⒋判虮矸譃楠毩⒌膬刹糠?#xff0c;前半部分小于pivot,后半部分大于等于pivot。然后遞歸這兩個子部分,直到每部分只有一個元素或者空為止。
現(xiàn)在出現(xiàn)了一個主要問題,如何使得待排序表能夠被劃分成兩部分,其中一部分小于pivot,另一部分大于等于pivot?這個過程我們把它稱為partition。
上述圖的過程很清晰的展現(xiàn)了快速排序一個partition的過程。
代碼實現(xiàn):
快速排序的性能分析較為復(fù)雜,由于快速排序是遞歸的(實際上所有的遞歸算法都可以變化成非遞歸),需要借助一個遞歸工作棧來保存每層遞歸調(diào)用的必要信息,其容量應(yīng)與遞歸調(diào)用的最大深度一致。最好情況下為O(log2n),最壞情況下為O(n)
時間復(fù)雜度:最佳情況為O(n * logN),提升快排算法的方法,pivot盡量選擇為中間大小的數(shù)字,使得能夠平分兩個子過程。
穩(wěn)定性:快速排序算法是不穩(wěn)定的。
快速排序算法的partition思想特別重要,它可以在排序的同時,準(zhǔn)確定位到排序表中特定位置的元素,劃分算法一定要想到快速排序的思想,下面舉一個題目的例子。
找出一個無序表中第k小的元素
選擇排序
基本思想:每一趟(第i躺),在后面n - i + 1個待排序元素中選取關(guān)鍵字最小的元素,作為有序子序列的第i個元素,直到n - 1躺做完,待排序元素只剩一個,排序結(jié)束。
選擇排序和冒泡排序的差不多,都是在,每一趟中網(wǎng)有序子序列中添加一個元素。
簡單選擇排序
思路:假設(shè)我們有排序表L[1…n],第 i 躺排序從L[i…n]中選出一個最小的,將其與第 i 位交換,每一趟都可以確定一個元素的最終位置,并且保證已排的子序列有序。
代碼實現(xiàn):
時間效率:我們可以發(fā)現(xiàn),簡單選擇排序的比較次數(shù)與序列的初始狀態(tài)無關(guān),始終為n (n - 1) / 2
空間效率:O(1)
穩(wěn)定性:不穩(wěn)定
堆排序
堆排序可以說是和快速排序同樣重要的一種排序。
堆排序?qū)⒁粋€數(shù)組看成一棵完全二叉樹(關(guān)于完全二叉樹的定義請自行百度,篇幅有限,不再贅述)。第i個結(jié)點的左孩子的序號是2i + 1,
右孩子的序號為2i + 2,他的雙親結(jié)點的序號為(i - 1)/ 2.
一個特殊的結(jié)點為數(shù)組中序號為0的結(jié)點,它的雙親結(jié)點還是自己,所以能夠和其他結(jié)點之間進(jìn)行區(qū)分。
知道了堆以后,還需要明白兩個概念:大根堆和小根堆
大根堆:所有子樹的根結(jié)點都是最大的
L[i] >= L[2i + 1] && L[i] >= L[2i + 2]
小根堆:所有子樹的根結(jié)點都是最小的
L[i] <= L[2i + 1] && L[i] <= L[2i + 2]
上述圖展現(xiàn)了一個大根堆,所有樹的根結(jié)點必然最大,并且左右孩子結(jié)點的大小不定。
那么知道啥叫大根堆以后,我們又如何來根據(jù)這一特殊的結(jié)構(gòu)來進(jìn)行排序呢?
首先我們拿到一個順序表,將其轉(zhuǎn)化成一個大根堆,那么我們可以發(fā)現(xiàn)大根堆的根結(jié)點一定是最大的元素(這點極其重要!!!)
然后我們將大根堆的堆頂(就是根結(jié)點)和最后一個元素交換,將這個位置固定。此時,我們看上圖,假設(shè)我們將100和7交換位置,那么此時的堆就不再是大根堆了,需要將其再次轉(zhuǎn)換成大根堆,但是注意的是100的位置我們就不需要去動了。
也就是說,堆排序一趟排序確定了當(dāng)前堆中最大的元素,并將其放到堆的最后一個位置,n - 1躺過后必然已經(jīng)稱為一個有序的數(shù)組。
現(xiàn)在有兩個問題:
上述代碼中heapSort中使用了一個for循環(huán),這個for循環(huán)的作用是,將第i位元素添加到堆中,并且保證堆是一個大根堆。
代碼的靈魂在于heapHelper方法,當(dāng)新加入的元素比自己的雙親結(jié)點大的時候,將其互換,一直持續(xù)到,自己的某個雙親結(jié)點不必自己大了,就跳出循環(huán)。
這里有個特殊的點是0,當(dāng)index = 0的時候,我們發(fā)現(xiàn)(index - 1)/2
依舊是0,所以二者相等,跳出循環(huán)。
好了,至此,我們已經(jīng)掌握了一個數(shù)組轉(zhuǎn)化成大根堆的方法了。
接下來的問題就是將堆頂元素與堆中最后一個元素交換以后,怎么將它再次的變成一個大根堆。
這里我們著重論述一下heapify的過程,heaplify有三個參數(shù),第一個是待排序的數(shù)組,第二個是開始大根堆化的根結(jié)點index(一般都是0),第三個是標(biāo)識當(dāng)前堆大小的heapSize。
首先獲取當(dāng)前結(jié)點的左孩子結(jié)點left(通過對應(yīng)的序號關(guān)系就可以得到),當(dāng)left并未超過當(dāng)前堆長度的時候,我們開始循環(huán)。
思路是找到index結(jié)點的左右孩子中較大的那個,給largest賦值的這句長代碼就是完成了這個任務(wù)。
第二步比較 index根結(jié)點 與 左右孩子結(jié)點中較大值 之間的大小 并重新給largest賦值
第三步 做判斷 如果largest就等于index根結(jié)點,說明 index根結(jié)點本身就是最大的,不需要往下交換了。否則,就需要和較大結(jié)點之間做交換 ,并且給index重新賦值,進(jìn)行下一次循環(huán)。
上面的代碼我覺得是較容易理解的版本了,邏輯和變量語義化來看都非常好。
關(guān)于堆排我們已經(jīng)完成了最主要的兩個任務(wù)了,下面是堆排序的完整代碼。
值得注意的是,當(dāng)我們每次將根結(jié)點(最大值)swap到最后的時候,需要通過–heapSize使得堆的長度減1來固定這個最大值。
空間效率:O(1)
時間效率:O(n * logn)
穩(wěn)定性:不穩(wěn)定的排序算法
堆排序的一些常考的注意點
堆排建堆的時間效率不會超過4n(雖然我也不知道咋算出來的)。
在尾部插入一個元素或者從堆頂彈出一個元素都是 logN的時間效率。
當(dāng)我們需要在大量數(shù)據(jù)中找出k個最大或者最小的元素時都采用堆排序。
歸并排序與基數(shù)排序
歸并排序的思想獨樹一幟,它是將長度為n的待排序列表看成 n 個小組,兩兩和并 使其有序,這樣我們就獲得了【n / 2】(向上取整)個有序的小組,這個過程被稱為一趟2路歸并,直到合并成長度為n的有序序列。
空間效率:需要空間為n的輔助數(shù)組O(n)
時間效率:O(n * logN)
穩(wěn)定性:穩(wěn)定的排序算法
基數(shù)排序
基數(shù)排序較為奇特,一般是基于鏈表來實現(xiàn)的一種排序,它不基于移動和比較,而是將單個關(guān)鍵字轉(zhuǎn)換成多個關(guān)鍵字進(jìn)行排序的方法。
比如對于一群小于1000的數(shù)字來說,每個數(shù)字可以看成有個,十,百三個關(guān)鍵字組成。于是我們可以把數(shù)字一個關(guān)鍵字,拆分成個,十,百三個關(guān)鍵字。
基數(shù)排序包含兩種方法,第一種是最高位優(yōu)先(MSD)法,按關(guān)鍵字位權(quán)重遞減一次逐層劃分成若干更小的子序列,最后將子序列依次連接。第二種最低位優(yōu)先法,與上個方法是相反的過程。
那么基數(shù)排序具體如何操作呢?下面以最低位優(yōu)先法舉例。
First,先定一個基數(shù)r,這個基數(shù)r代表了拆分出來的關(guān)鍵字集合(如上述的個,十,百位)中可能取值的個數(shù)。比如個,十,百,他們的取值可能都是0 ~ 9,所以取值個數(shù)為10,那么就把r定為十。
Second,定義r個隊列,并將他們置空。
Then,進(jìn)行分配和收集。
啥叫分配和收集?
我們有多少個拆分出來的關(guān)鍵字,就需要多少回分配和收集。
一次分配的過程如下:一一掃描線性表中的結(jié)點,發(fā)現(xiàn)了關(guān)鍵字與隊列對應(yīng)的取值相等,那么就將該結(jié)點添加進(jìn)這個隊列。比如我們首先開始個位數(shù)關(guān)鍵字的分配,總共r(r = 10)個隊列分別對應(yīng)0 ~ 9,我們掃描鏈表中的結(jié)點的時候,發(fā)現(xiàn)個位數(shù)等于5那么就加到對應(yīng)5的隊列中去。
一次收集的過程如下:將剛剛被分配完畢的隊列依次首尾相連獲得一個新的隊列。
一次收集和分配也被稱為一趟排序的過程。
基數(shù)排序剛剛接觸可能覺得有點奇怪,但是把上面的圖看懂了以后應(yīng)該就能大致理解,基數(shù)排序到底在干什么了。
基數(shù)排序代碼實現(xiàn)一般不會考察,我們只需要它的過程就可以。
空間復(fù)雜度:O?,r個隊列
時間復(fù)雜度:d躺分配收集O(d),一次分配是O(n),一次收集是O?
所以時間復(fù)雜度是O(d(n + r))
穩(wěn)定性:因為隊列先入先出的特點,是穩(wěn)定的排序算法。
總結(jié)
八大內(nèi)部排序算法內(nèi)容還是不少的,在記憶時,應(yīng)著重于原理,時間復(fù)雜度,空間復(fù)雜度,算法穩(wěn)定性,適用場景幾個方面進(jìn)行記憶。
下面進(jìn)行一個總結(jié)。
冒泡排序算法總結(jié)
原理:兩兩比較相鄰的記錄,向左或者向右冒泡,一趟排序可以確定一個元素的最終位置。
時間效率:冒泡排序的時間效率是和待排序列的初始狀態(tài)有關(guān)的。
最好的情況下,初始列表有序:
比較次數(shù)為n - 1次,移動次數(shù)為0次,時間復(fù)雜度為O(n)
最壞情況下,初始列表無序:
比較次數(shù):第i趟排序需要比較n - i次 i從1 到n - 1求和為 n(n - 1) / 2
移動次數(shù):3n(n - 1)/2 移動次數(shù)是比較次數(shù)的三倍
時間復(fù)雜度:O(n2)
空間效率:O(1)
算法穩(wěn)定性:因為每次比較,如果兩個元素相等,那么就不會發(fā)生交換,所以是穩(wěn)定的排序算法。
適用場景:冒泡排序是和待排序列的初始條件有關(guān)的,所以當(dāng)待排序列幾乎是有序的,那么冒泡排序的時間是線性的,可以使用。
快速排序算法總結(jié)
原理:快排的基本思想是基于分治的,從待排序列中挑出一個基準(zhǔn),將整個序列分成小于基準(zhǔn)的,大于等于基準(zhǔn)的兩部分。一趟排序可以確定一個元素的最終位置。快速排序?qū)τ趧澐謹(jǐn)?shù)組來說有奇效,比如我要找一個數(shù)組中第k大的數(shù)字,相當(dāng)于就把數(shù)組劃分成了比k小的部分和比k大的部分。再比如把數(shù)組較小的一半和較大的一半分開等等。
時間效率:快排的時間效率是和partition分不開的,究其根本就是pivot的選定問題,如果partition能使得劃分出的兩部分均一,那么時間效率就會較高。
最壞情況:pivot選的太大或者太小此時的時間復(fù)雜度為O(n2)
最好情況:pivot選的正中間,此時的時間復(fù)雜度為O(n * log n)
空間效率:快排的空間效率同樣和pivot的選定有關(guān)
最壞情況:調(diào)用棧的深度為O(n)
最好情況:調(diào)用棧的深度為O(n * log n)
算法穩(wěn)定性:不穩(wěn)定
適用場景:當(dāng)n較大,關(guān)鍵字趨近于隨機(jī)分布,不要求穩(wěn)定性的時候,那么用的一定是快排,因為快排被認(rèn)為是效率最高的內(nèi)部排序算法。
插入排序算法總結(jié)
原理:子序列已經(jīng)排好,插入排序就是把無序序列中的元素,往前面排好的子序列里面插入。直接插入與折半插入的區(qū)別在于定位插入的位置,也就是比較元素的次數(shù)不同。希爾排序規(guī)定了步長,在對應(yīng)步長的子序列中應(yīng)用直接插入排序,并一步步縮短步長直至為1.
時間效率:關(guān)于直接插入排序的比較次數(shù)和移動次數(shù)我是存在疑慮的,在嚴(yán)蔚敏的教材里把哨兵的比較也算了進(jìn)去,至于什么是哨兵其實我也不太理解。但是我認(rèn)為直接插入排序的時間效率是和冒泡的情況類似的。
最好的情況下,初始列表有序:
比較次數(shù)為n - 1次,移動次數(shù)為0次,時間復(fù)雜度為O(n)
最壞情況下,初始列表無序:
比較次數(shù):第i趟排序需要比較n - i次 i從1 到n - 1求和為 n(n - 1) / 2
移動次數(shù):3n(n - 1)/2 移動次數(shù)是比較次數(shù)的三倍
時間復(fù)雜度:O(n2)
空間復(fù)雜度:O(1)
算法的穩(wěn)定性:穩(wěn)定的
適用場景:n較小或者待排數(shù)列基本有序,仔細(xì)分析算法的化,直接插入排序肯定是比冒泡排序好的,所以遇到兩個都可以用的時候,要選直接排序。此外直接排序可以用鏈表結(jié)構(gòu)實現(xiàn),當(dāng)所含記錄數(shù)據(jù)量龐大,移動記錄耗費大量時間,采用鏈表作為存儲結(jié)構(gòu)。
簡單選擇排序算法總結(jié)
原理:第i趟在后面n - i + 1個待排序元素中選取關(guān)鍵字最小的元素,作為有序子序列的第i個元素,直到n - 1趟完成。簡單選擇排序同樣也是每一趟排序就能確定一個元素的最終位置。
時間效率:比較次數(shù)是一定的,與數(shù)據(jù)原始狀態(tài)無關(guān),為n(n - 1)/2.元素移動次數(shù)不定。最好情況下,待排序列有序,無序移動。
最壞情況下,待排序列逆序,移動次數(shù)3* (n - 1),所以時間復(fù)雜度始終是O(n2)
穩(wěn)定性:因為每次找到最小值以后需要交換位置,可能會把數(shù)字相同在前的換到后面去。,所以是不穩(wěn)定的。
適用場景:簡單排序移動元素的次數(shù)少,所以當(dāng)n比較小的時候,元素的信息量較大,那么就可以使用簡單選擇排序。
堆排序算法總結(jié)
原理:堆排序是把數(shù)組按照下標(biāo)的關(guān)系視作了一棵完全二叉樹,我們排序所用到的堆是大根堆。使用堆排序?qū)?shù)組進(jìn)行排序的時候首先將數(shù)組轉(zhuǎn)化成大根堆,從第一個非葉子節(jié)點開始篩選,直至變成大根堆。獲得大根堆以后,每次將堆頂?shù)脑嘏c最后的元素交換位置,重新的構(gòu)造大根堆。堆排序每次可以確定最后一位元素的位置。
時間效率:建堆的時間為O(n),添加新元素,彈出堆頂,調(diào)整大根堆的時間都是O(logn),最好,最壞和平均情況都是O(N * logN)
空間效率:O(1)
穩(wěn)定性:不穩(wěn)定的排序
適用場景:通常,取一大堆數(shù)據(jù)中k個最大或最小元素時都優(yōu)先采用堆排序
歸并排序算法總結(jié)
原理:我們說的歸并排序一般都是2路歸并排序,歸并排序一般是將兩個有序表歸并為一個有序表,所有用歸并排序的思想可以解決兩個兩個有序表合并的問題。歸并排序每個數(shù)都可以找到比自己大的數(shù),所以還可以用來解決小和,逆序?qū)?/strong>問題。
時間效率:每次歸并的時間復(fù)雜度是O(n),與原始狀態(tài)無關(guān),需要進(jìn)行【logn】次歸并(向上取整),所以時間復(fù)雜度就是O(n * logN)
空間效率:O(N)
穩(wěn)定性:當(dāng)左右兩邊元素相等的時候,右邊的先入輔助表,所以不改變元素之間的相對位置,是穩(wěn)定的。
適用場景:當(dāng)n較大的時候,需要使用O(n * logN)排序算法,如果還要求穩(wěn)定性,這個時候可以使用歸并算法。
基數(shù)算法總結(jié)
基數(shù)算法上面的總結(jié)的很到位了,這里做個補充,當(dāng)n很大的時候,關(guān)鍵字位數(shù)較燒且可以分解的時候,我們可以采用基數(shù)排序。如按照出生日期的月,日來給中國人排序。
總結(jié)
以上是生活随笔為你收集整理的内部排序算法全面总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【EMC电磁兼容】01.09——EMC中
- 下一篇: AD/DA转换器