資料結構:直接插入排序 & 希爾排序 & 選擇排序 & 堆排序 & 氣泡排序 & 快速排序 & 歸併排序
一、什麼是排序
排序就是將一組雜亂無章的資料按照一定的次序組織起來,此次序可以是升序也可以是降序
二、為什麼需要進行排序
為了滿足一些需求,比如在比較學生的成績時,我們就需要給所有學生的成績排一個順序,這樣才方便我們檢視學生的名詞,所以說排序就是為了給我們生活中的事提供方便。
三、資料表
資料表就是待排序資料元素的有限集合
四、排序碼&主排序碼&次排序碼
(1)排序碼
排序時需要按照一定的規則,才能夠排序,而排序碼就是規則,比如比較兩個學生的名次時,可以用綜合成績來比較,此綜合成績就是排序碼
(2)主排序碼 & 次排序碼
如果一個數據表中各個元素的排序碼有多個,比如學生的屬性有學號、成績、姓名、班級等,在進行排序時,這些屬性都可以作為排序碼,如果使用一個排序碼排序出來的結果是唯一的,那此排序碼就是主排序碼,如果使用一個排序碼排序出來的結果不唯一,那此排序碼就是次排序碼。就像學生,使用學號進行排序時,排序結果是唯一的,那學號就是主排序碼,因為一個學生的學號是唯一的,而如果使用成績進行排序時,排序結果可能不唯一,因為一個分數可能會有好幾個人,誰的名次在前,誰的名次在後,都是不確定的,那成績就是次排序碼
五、排序演算法的穩定性
如果在一個排序表中,有兩個元素R[i]==R[j],它們的排序碼K[i]==K[j],在排序之前,元素R[i]在元素R[j]的前面,排序之後,元素R[i]還是元素R[j]的前面,那麼此演算法就是穩定的,否則就認為這個演算法不穩定
例如對{1,2,4,7,8,9,5,5,6}進行排序,資料表中有兩個5,下標為6的5在下標為7的5前面,如果排序之後,下標為6的5還是在下標為7的5前面,那就說此排序演算法是穩定的,否則是不穩定的。
ps:在排序時,如果牽扯到間隔的交換兩個元素的話,那此演算法就是不穩定的,例如直接選擇排序演算法就是不穩定的。如果是挨著交換的話,那就認為此演算法是穩定的,例如氣泡排序,是緊挨著的元素的兩兩交換,所以冒泡演算法是穩定的。例如在進行快速排序就是不穩定的,因為會牽扯到間隔的元素的交換。
六、內部排序&外部排序
(1)內部排序
當在進行排序時,需要進行排序的資料可以一次性的載入到記憶體
(2)外部排序
與內部排序相反,需要進行排序的資料需要多次的載入到記憶體,不能一次性的載入到記憶體。
七、排序演算法
1.直接插入排序
演算法描述
當一個序列進行排序時,把序列分成兩個部分,有序和無序,預設第一個元素是有序的,依次把無序序列中的元素通過與有序序列中元素的比較,搬移到有序序列中。
ps:B樹在插入資料時,就是使用的插入排序
(1)簡單版的插入排序(對資料按升序進行排序)
方法:比較一個元素搬移一個元素
void InsertSort(int array[], int sz)//插入排序
{
int i = 0;
int end = 0;//標記有序序列中的最後一個元素
for (i = 1; i < sz; i++){
end = i - 1;//預設陣列的第一個元素是有序的
int key = array[i];//key儲存的是待排序的元素
while (key < array[end] && end >= 0){
array[end + 1] = array[end];
end--;
}
//在while迴圈中,也可以把key<array[i]寫為key<=array[i],但
//是如果這樣寫的話,會使此演算法不穩定
array[end + 1] = key;
}
}
(2)使用二分查詢來尋找插入位置(對資料按升序進行排序)
優點:在尋找插入位置時,比較的次數少
方法:使用二分尋找位置,然後搬移
//使用二分查詢查詢插入位置的插入排序
void InsertSort_Binary(int array[], int sz)
{
for (int i = 1; i < sz; i++){
int key = array[i];
//1.第一步:使用2分查詢演算法找到待插入元素需要插入的位置
int left = 0;
int right = i-1;
while (left <= right){
int mid = left + ((right - left) >> 1);
if (key < array[mid])
right = mid - 1;
else
left = mid + 1;
}
//2.第二步:插入待插入的元素(搬移left後面到i之前所有的元素)
right = i - 1;
//注意right==left的元素也需要搬移,因為此時在上面迴圈的結束
//條件是left>right,所以left多往後面動了一下
while (right >= left){
array[right + 1] = array[right];
right--;
}
array[left] = key;
}
}
ps:這兩個方法搬移的次數一樣,但是使用二分查詢,會使比較的次數少,從而提高排序的效率
(3)複雜度
時間複雜度:O(N) 到 O(N^2)之間
空間複雜度:O(1)
(4)插入排序的應用場景
應用場景:
a.需要排序的資料序列已經接近有序;
b.資料量少最差場景
給的資料序列是降序,但是需要一個升序序列
最優場景
給的資料序列是降序,正好需要的也是一個降序序列
(5)插入排序演算法是穩定的排序演算法
2.希爾排序
ps:此排序時插入排序的升級版,當插入排序處理的資料量大時,採用希爾排序
演算法描述
(1)程式碼
void ShellSort(int array[], int sz)//希爾排序
{
int gap = sz / 3 + 1;//gap是控制分組的,例如gap=3,那
//就是3個元素為一組
int i = 0;
while (gap){
for ( i = gap; i < sz; i++){
int end = i-gap;//標記有序序列中的最後一個元素
int key = array[i];//待插入元素
while (key < array[end] && end >= 0){
array[end + gap] = array[end];
end -= gap;
}
array[end + gap] = key;
}
--gap;
}
}
(2)希爾排序的應用場景
資料量大的時候,可以採用希爾排序
(3)希爾排序不穩定,因為排序時元素是隔著交換的
(4)複雜度
時間複雜度
與gap的取值規則有關,
時間複雜度一般集中在N^1.25到1.6N^1.25之間
,當gap=(元素個數)/3+1時,希爾排序的效率最高空間複雜度:O(1)