三種基本排序

2021-07-11 09:44:52 字數 3869 閱讀 2403

由於排序不僅是針對主關鍵字,那麼對於次關鍵字,因為待排序的記錄序列中可能存在兩個或者兩個以上的關鍵字相等的記錄,排序結果可能會存在不唯一的情況,所以我們給出了穩定與不穩定排序的定義。

假設ki = kj(1<=i<=n, 1<=j<=n, i != j),且在排序前的序列中 ri 領先於 rj (即i < j)。如果在排序後仍然領先,則稱所用的排序方法是穩定的;反之,若可能使得排序後的序列中 rj 領先於 ri,則稱使用的排序方法是不穩定的。

根據在排序過程中待排序的記錄是否全部被放置在記憶體中,排序分為:內排序和外排序。

內排序是在排序整個過程中,待排序的所有記錄全部被放置在記憶體中。外排序是由於排序的記錄個數太多,不能同時放置在記憶體,整個排序過程需要在內外存之間多次交換資料才能進行。我們這裡主要介紹內排序的多種方法:

對於內排序來說,排序演算法的效能主要是受3個方面的影響:

1、時間效能

在內排序中,主要進行兩種操作:比較和移動。比較指關鍵字之間的比較,這是要做排序最起碼的操作。移動指記錄從乙個位置移動到另乙個位置,事實上,移動可以通過改變記錄的儲存方式來予以避免。總之,高效率的內排序演算法應該具有盡可能少的關鍵字比較次數和盡可能少的記錄移動次數。

2、輔助空間

評價排序演算法的另乙個主要標準是執行演算法所需要的輔助儲存空間。輔助儲存空間是除了存放待排序所占用的儲存空間之外,執行演算法所需要的其他儲存空間。

3、演算法的複雜性

這裡指的是演算法本身的複雜度,而不是指演算法的時間複雜度。顯然演算法過於複雜也會影響排序的效能。

為了方便後面的講解,我們先提供乙個用於排序的順序表結構,此結構也將用於之後我們要講的所有排序演算法。

#define maxsize 10   //可自行更改大小

typedef

struct sqlist;

另外最常用的就是交換了,我們也寫成乙個函式,方便使用

void swap(sqlist *l, int i, int j)
接下來開始第一種排序

氣泡排序的基本思想是:兩兩比較相鄰記錄的關鍵字,如果反序則交換,直到沒有反序的記錄為止。冒泡細分下來可以分為3種,先來看最簡單的一段。

void bubblesort0(sqlist *l)
嚴格來說,不算是標準的氣泡排序,因為它不滿足「兩兩比較相鄰記錄」的思想,應該算是交換排序。思路就是和讓每乙個關鍵字和後面的比較,如果大就交換。這個應該算是最容易寫出來的排序**了,但是它卻有缺陷。

例如 在排序好1,2的位置後,對其餘的關鍵字的排序沒什麼幫助,3反而還被換到了最後,也就是說,這個演算法的效率是非常低的。想知道具體交換次序的可以在排序函式裡面用列印函式看看每一次交換後陣列中是怎麼回事。

我們來看看真正的氣泡排序到底有何改進

void bubblesort1(sqlist *l)
因為從後往前交換,所以,當 i = 0時,1就到了最上面,2也到了第三個位置。往後的話,資料交換更簡單,在越多的資料排序中,這種優勢就更能體現出來,較小的數字如同氣泡一樣慢慢浮到上面,因此就將該演算法命名為冒泡演算法。此處,只提供第一次排序的示意圖,全部過程就自己和上面一樣,用輸出函式自己列印觀察了。

當然這樣的氣泡排序是還可以優化的。如果我們的待排序序列式 ,也就是說,除了1和2意外的,都已經是正常的順序了,那麼,當 i = 1時,交換了2和1,此時序列已有序,但是演算法依舊會將從 i = 2到9所有的都執行了一遍,儘管沒有交換,但是大量的比較還是多於的。為了改變這個現狀,我們使用乙個flag來改進:

void bubblesort2(sqlist *l)}}

}

**改動的關鍵就在於 i 變數的迴圈中增加了flag,可以避免一些無意義的迴圈,在效能上就有一些提公升。

當最好的情況下,如果本來就是有序的,那麼根據改進後的**,就有 n-1 次的比較,沒有交換,時間複雜度為 o[n],當最壞的情況,即待排序的表是逆序的情況,此時需要比較 n(n-1)/2 次,並作等數量級的記錄移動,因此總的時間複雜度是o[n^2]。

簡單選擇排序法就是通過 n - i 次關鍵字間的比較,從 n - i + 1個記錄中選出關鍵字最小的記錄 ,並和第 i (1<=i<=n) 個記錄交換,我們來看**:

void insertsort(sqlist *l)

if (i != min)

swap(l, i, min);}}

這段**不難理解,我們也以 為例,對 i 從1迴圈到8.當 i = 1時,l->arr[i] = 9,min開始是1,然後與 j = 2到9比較大小,因為 j = 2時最小,所以min = 2。最終交換了l->arr[1]與l->arr[0]的值,如圖所示,注意,比較8次,卻只交換資料操作一次。

後面的也是乙個道理,如果想看每一次的處理結果,可以在迴圈裡面加輸出函式,就不多說了,這組排序只需要8次就可以完成。

從簡單選擇排序的過程來看,它最大的特點就是交換移動資料次數相當地少,也就節約了時間。分析它的時間複雜度分析可以發現,無論是最好還是最壞的情況,需要比較的次數都是一樣多的,第 i 趟排序需要進行 n - i 次關鍵字的比較,此時需要比較 n(n-1)/2 次。而對於交換次數來說,最好的時候只需要0次,最差的時候,需要 n-1 次,最好的排序時間應該是比較和交換的總和,因此,總的時間複雜度也是 o[n^2] ,雖然說複雜度和氣泡排序是一樣的,但是效能上還是要略優於氣泡排序的。

直接插入排序的基本操作是將乙個記錄插入到已經排好序的有序表中,從而得到乙個新的、記錄數+1的有序表。下面我們直接來看**。

從這個每次插入排序後的序列順序,我們可以看到,從 i = 1開始比較,使用temp記錄 arr[i] 的資料,然後乙個for迴圈遍歷 i 之前的資料,找到合適位置,在遍歷的同時,將不滿足條件的位置上的數依次後移,最後,將temp(也就是arr[i])放在該插入的位置。這就是直接插入排序的基本思路。

從空間上來看,只需要乙個記錄的輔助空間,所以可以忽略,關鍵是看時間複雜度。

1、當最好的情況,也就是要排序的表本身就是有序的,那麼比較次數就是 n-1 次,而且每次都是l->arr[i] > l->arr[i-1],所以也就沒有移動記錄(不需要temp),即時間複雜度為 o[n] 。

2、當最壞的情況,也就是本身是逆序的,那麼此時的比較次數就是 (n+2)(n-1)/2 次,記錄的次數也達到了最大值 (n+4)(n-1)/2 次。

3、當排序是隨機的情況,那麼概率是平均的,也就是說平均比較和移動的次數約為 (n^2)/4 次,因此,我們得出直接插入排序的時間複雜度為 o[n^2] 。當然我們也可以看出,同樣是 o[n^2] 的時間複雜度,但是直接插入排序的效能比前面兩個都要好。

以上就是三種基本排序,後續的學習中還會繼續補充其他的排序方法。

三種基本排序方法

三種基本排序方法 includevoid func1 int a 10 交換排序法 void func2 int a 10 選擇排序法 void func3 int a 10 氣泡排序 int main int choice printf 請輸入你要選擇的排序方法 n scanf d choice ...

三種基本排序演算法

示例陣列 以公升序為例 for int i 0 i arr.length i 輪數 第一次 j下標從0開始,第0位與第1位相比較,當第0位大於第1位時 3 1 進行交換。交換完陣列為 第二次j為1,第1位與第2位比較,3 5,不交換。第三次j為2,第2位與第3位比較,5 2,交換。陣列為 第四次j為...

三種基本的資料排序方法

package com.beyondlife.demo2 這個類擁有三個靜態方法用於陣列排序 public class numbersort end if end inner for end outter for else if type des end if end inner for end o...