經典十大排序演算法

2022-06-29 14:33:14 字數 3908 閱讀 6673

排序種類繁多,大致可以分為兩大類:

了解乙個概念:排序的穩定性

穩定是指相同大小的元素多次排序能保證其先後順序保持不變。假設有一些學生的資訊,我們先根據他們的姓名進行排序,然後我們還想根據班級再進行排序,如果這時使用的時不穩定的排序演算法,那麼第一次的排序結果可能會被打亂,這樣的場景需要使用穩定的演算法。

堆排序、快速排序、希爾排序、選擇排序是不穩定的排序演算法,而氣泡排序、插入排序、歸併排序、基數排序是穩定的排序演算法。

大多數人學程式設計接觸的第一種排序,名稱很形象。每次遍歷排出乙個最大的元素,將乙個最大的氣泡冒出水面。

public static void bubblesort(int arr) }}

}

最直觀易理解的排序演算法,每次排出乙個最小的元素。也是最穩定的演算法,時間複雜度穩定為o(n^2)。需要乙個變數記錄每次遍歷最小元素的位置。

public static void selectsort(int arr)

}int tp = arr[maxidx];

arr[maxidx] = arr[n - 1 - i];

arr[n - 1 - i] = tp;}}

一種直觀的排序演算法,從第二個元素開始,每次往前面遍歷找到自己該在的位置。

public static void insertsort(int arr)}}

是第乙個突破o(n^2)複雜度的演算法,是插入排序改進版,又稱縮小增量排序。選擇乙個增量g,每次將資料按g分隔分成g組,每次對組內進行插入排序。每組排完後,將g/2繼續直到g=1。一般選擇g=len/2,但這不是最優選擇,還有很多增量選擇方案。

public static void shellsort(int arr)}}

}

是氣泡排序的一種改進演算法。

基本思想是:

在資料集之中,選擇乙個元素作為"基準"(pivot)。

所有小於"基準"的元素,都移到"基準"的左邊;所有大於"基準"的元素,都移到"基準"的右邊。

對"基準"左邊和右邊的兩個子集,不斷重複第一步和第二步,直到所有子集只剩下乙個元素為止。

經典的快排選擇第乙個數為基準。當排的是乙個近乎有序的序列時,經典快排會退化為\(o(n^2)\),這時採用隨機快速排序則比較恰當,隨機快排是在排序的數中隨機選擇乙個做基準。快排可以做到原地實現。

歸併的思想是先使每個子串行有序,再使得子串行合併後有序。與快排一樣,都基於分治思想,不過快排在於分,而歸併在於合。

是乙個穩定的演算法。一般歸併的比較次數少於快排,而移動次數多於快排。(所以這是計算機中多用快排的原因,移動資料的代價更高)。對於本身具有一定有序性的序列,可以採用改進的歸併,最好的情況能將複雜度降低至o(n).

歸併還有乙個用途:用於求逆序數。

《演算法》上看到的寫法,非常優雅:

static void sort(int arr, int lo, int hi, int temp)

static void merge(int arr, int lo, int mid, int hi, int temp)

}

這裡特指二叉堆,二叉堆是一種完全二叉樹,並且所有結點的值大於(小於)等於子節點的值,分別稱為大(小)頂堆。下面以大頂堆為例講述。

首先我們要知道,完全二叉樹和陣列可以相互對映,通過下標可以找到節點的子節點,節點i的左子節點為:2i+1;右子節點為:2i+2。通常我們排序的是陣列,就將其對映成完全二叉樹來實現。

堆排序就是給定了乙個無序的序列,先將其構造成乙個大頂堆,然後將大頂堆的堆頂元素與最後乙個元素交換,那麼第乙個數就排好了,然後逐個排序

完整**:

public static void heapsort(int arr) 

}private static void buildheap(int arr)

}private static void adjustheap(int arr, int i, int len)

if(rightarr[maxindex])

if(maxindex != i)

}private static void swap(int arr, int i, int j)

計數排序是非比較型排序,適用於具有確定範圍的序列。計數排序新建了乙個k=maxval-minval大小的陣列用來統計各個數出現次數。是乙個穩定的排序演算法,當序列比較集中時,它優於所有的比較排序演算法。但當\(o(k)>o(n*logn)\)時,效率反而不如比較型演算法。

public static void countsort(int arr) 

for (int i = 0, j = 0; i < space.length; i++) }}

劃分多個範圍相同的區間,每個子區間自排序,最後合併

桶排序是計數排序的擴充套件版本,計數排序可以看成每個桶只儲存相同元素。而桶排序每個桶儲存一定範圍的元素,通過對映函式,將待排序陣列中的元素對映到各個對應的桶中,對每個桶中的元素進行排序,最後將非空桶中的元素逐個放入原序列中。

一般的做法是,找到排序的序列所在區間,平均分成幾個區域(即幾個桶),然後根據對映關係將數逐一放入桶內,對每個入桶的資料在該桶內還要進行排序(可以自選排序演算法),然後依次取出桶中資料得到有序數列。

當資料服從均勻分布時,該演算法線性時間o(n),如果資料很集中,當所有資料集中在同乙個桶中時,桶排序就失去了意義。

桶排序的關鍵:

元素值域的劃分,也就是元素到桶的對映規則。對映規則需要根據待排序集合的元素分布特性進行選擇,若規則設計的過於模糊、寬泛,則可能導致待排序集合中所有元素全部對映到乙個桶上,則桶排序向比較性質排序演算法演變。若對映規則設計的過於具體、嚴苛,則可能導致待排序集合中每乙個元素值對映到乙個桶上,則桶排序向計數排序方式演化。

public static void bucketsort(int arr) 

int bucketnum = (max-min)/arr.length + 1;

list> bucketarr = new arraylist<>(bucketnum);

for (int i = 0; i < bucketnum; i++)

for (int i = 0; i < arr.length; i++)

for (int i = 0; i < bucketarr.size(); i++)

for (int i = 0, j = 0; i < bucketarr.size(); i++) }}

基數排序是逐位進行排序的演算法。先按照低位先排序,排好後收集,再逐次公升高位數進行排序,收集,直到最高位,收集後即為有序序列。相當於用十個桶進行排序,又稱為桶子法。其排序效率取決於最大數的位數d,每位數字的分布範圍k(0-9),一般k=10。

public static void basesort(int arr) 

int mod = 10, dev = 1;

for (int i = 0; i < digits; i++, dev *= 10, mod *= 10)

for (int j = 0, index = 0; j < tong.size(); j++) }}

}

前述排序都是指直接在記憶體中可以完成的,即內部排序。外部排序是指資料量很大,需要多次從磁碟讀入的情況。

外部排序一般使用歸併演算法,為了提高效率,並且通常是多路歸併。

十大排序演算法

1.非線性時間類排序 時間複雜度未突破 0 nlog 2n 不穩定的四個排序演算法 選擇排序 0 n2 希爾排序 0 n1.3 希爾排序實現python 快速排序 0 n log2 n 0 nlog 2n 0 nlog 2 n 快速排序實現c python 堆排序 0 n log2 n 0 nlog...

十大排序演算法

排序規則 從左至右依次增大 一 基於比較的排序演算法 插入排序 逐個遍歷未排序序列,將其在已排序序列中從右到左比較,直到遇到比自己小的數,然後將元素插入到那個數的後面,有序序列從左向右生長。選擇排序 在未排序序列中選擇最小的元素,將其插入到已排序序列的末尾,有序序列從左向右生長。氣泡排序 從左至右逐...

十大排序演算法

github 排序演算法是 資料結構與演算法 中最基本的演算法之一。排序演算法可以分為內部排序和外部排序,內部排序是資料記錄在記憶體中進行排序,而外部排序是因排序的資料很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。常見的內部排序演算法有 插入排序 希爾排序 選擇排序 氣泡排序 歸併排序...