Sorting 排序詳解(c語言實現)#
今日突然有任務,明天補充完整。
郵箱:[email protected]期待交流。
Hello,各位小夥伴~我是你們的課代表橙橙,今天呢我要給大家分享的是關於內排序的四種算法:插入排序、交換排序、選擇排序、歸併排序。
我會先向大家詳細的敘述這四種排序算法的內容(推薦書籍
《大話數據結構》入門),然後再和大家一起細緻的去分析這幾種算法(推薦書籍 《算法4》深入瞭解 )。(備註:說到推薦算法類書籍,《算法導論》怎麼能少呢!但是奈何橙橙幾次下定決心去啃都啃失敗後,還是建議各位少俠先挑軟骨頭啃啦~)
想當年大二學數據結構的時候,完完全全的學渣一枚,那時也不咋聽課,竟然純真到覺得這些個排序算法有麻子用,浪費時間~~,當然然後就是哭唧唧了o(╥﹏╥)o。但在後來漸漸深入後,才明白,這玩意秒啊,排序在計算機中的應用非常廣泛,在SQL中更是一個重中之重的內容。
此外,撇開它應用的重要性,在教材裏的8大經典排序算法中,每種解法背後都包含了不同的知識點。要說簡單,二分查找也是非常簡單的,但是套路非常固定,時間複雜度爲固定的nlogn,無法優化。然而排序算法光是書上就有8種,每一種就像CF中的一把把槍,背後都有各自的特點:比如插入排序代表直觀的人類思維(平時打牌時的整理牌序)如一把樸素的武器;歸併排序背後的分治思想,像可以升級多次的高潛力武器;堆排序借用二叉堆,像在皇級武器;快速排序正如其名一樣高效,就像一個熱門的高傷害重炮,身爲一個程序員不能夠隨手就敲出個快速排序的話還是得好好思考一下自己。而快速排序本身也存在優化,就像對武器進行定製。
在開始介紹算法前,我們先梳理幾個關於排序的基本概念:
- 排序中的數據元素被稱爲記錄;
- 排序的穩定性 : 是評判一個算法的重要指標
如果一組記錄排序前爲(按照字母順序排序):
服裝,安慕希,凳子,包子,燈籠,鱷魚,菜包
排序後爲:
安慕希,包子,菜包,凳子,燈籠,鱷魚,服裝
我們可以清晰地看到,排序前後“凳子”和“燈籠”的相對位置沒有發生改變,則稱我們本次的排序方法是穩定的;反之,爲不穩定的。
- 內、外排序
內排序: 排序的整個過程,待排序的記錄全部被放在內存中;
外排序:待排序的記錄的個數太多,不能同時放置在內存中,整個排序過程需要在內外存之間多次交換數據才行。
注:本篇博客只講內排序。 - 算法的性能
- 時間性能
- 輔助空間
- 算法的複雜性
(對這部分有問題的可以去複習一下數據結構的課本)
接下來介紹的算法有:
- 冒泡排序(簡化版、正宗版)
- 選擇排序(簡單選擇排序、堆排序)
- 插入排序(直接插入排序、希爾排序)
- 分治思想法排序(歸併排序、快速排序)
在算法詳解前,先貼出完整的代碼,大家可以根據代碼來看算法,這樣可能會更加容易理解。
/*Author:Kim
**郵箱:[email protected]
**Topic:Sorting
**如果我的代碼或者博客對你有一絲幫助的話,麻煩給我點個贊相互鼓勵一下
*/
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
#define true 1
#define false 0
// 數據結構準備
typedef struct {
int r[MAXSIZE + 1];// r[0]用作哨兵或者臨時變量
int length;
}SqList;
// 排序 = 比較 +移動
// 移動函數
void swap(SqList* L, int i, int j) {
int k;
k = L->r[i];
L->r[i] = L->r[j];
L->r[j] = k;
}
void show(SqList *L) {
int i;
for (i = 1; i < L->length; i++)
printf(" %d", L->r[i]);
}
//-------------------------------------------------
//冒泡排序
void BubbleSort0(SqList* L) {
int i, j;
for (i = 1; i <= L->length; i++){
for (j = i+1; j <= L->length; j++){
if (L->r[i] > L->r[j]) {
swap(L,i, j);
show(L);
printf("\n");
}
}
}
}
void BubbleSort(SqList* L){
int i,j;
// 用來檢測,當記錄已經有序時則結束循環
int flag = true;
for(i=1;i<=L->length&&flag;i++){
flag = false;
for(j=L->length;j>i;j--){
if(L->r[j]<L->r[j-1]){
swap(L,j,j-1);
flag = true;
}
}
show(L);
printf("\n");
}
}
void SelectSort(SqList* L){
int i,j,min;
for(i=1;i<=L->length;i++){
min = i;
for(j=i+1;j<=L->length;j++){
if(L->r[min]>L->r[j])
min = j;
}
if(min!=i) swap(L,min,i);
show(L);
printf("\n");
}
}
void InsertSort(SqList* L){
int i,j;
for(i=2;i<L->length;i++){
if(L->r[i]<L->r[i-1]){
L->r[0] = L->r[i];
for(j=i-1;j>0&&L->r[0]<L->r[j];j--){
L->r[j+1] = L->r[j];
}
L->r[j+1] = L->r[0];
}
show(L);
printf("\n");
}
}
void ShellSort(SqList* L){
int i,len=L->length;
do{
len = len/2+1;
for(i=1;(i+len)<=L->length;i++){
if(L->r[i]>L->r[i+len])
swap(L,i,i+len);
}
}while(len!=1);
printf("已排成一個大致有序的序列\n");
show(L);
printf("進行簡單插入排序:\n");
InsertSort(L);
}
void HeapSort(SqList* L){
int i;
for(i=L->length/2;i>0;i--){
HeapAdjust(L,i,L->length);
}
for(i=L->length;i>=1;i--){
swap(L,1,i);
HeapAdjust(L,1,i-1);
show(L);
printf("\n");
}
}
void HeapAdjust(SqList* L,int s,int m){
int temp,j;
temp=L->r[s];
for(j=2*s;j<=m;j++){
if(j<m&&L->r[j]<L->r[j+1])
++j;
if(temp>=L->r[j])
break;
L->r[s] = L->r[j];
s=j;
}
L->r[s] = temp;
}
int main(void) {
int i;
//數據準備
SqList* L = (SqList*)malloc(sizeof(SqList));
L->length = 9;// 0號房間用來做哨兵或者臨時變量
int arr[10] = { 114,135,156,181,119,156,172,146,183,177 };
for (i = 0; i < 10; i++)
L->r[i + 1] = arr[i];
printf("排序前:\n");
show(L);
printf("\n");
//------------------簡化版冒泡排序---------------------
printf("冒泡排序:\n");
BubbleSort0(L);
//------------------正宗冒泡排序---------------------
for (i = 0; i < 10; i++)
L->r[i + 1] = arr[i];
printf("正宗冒泡排序:\n");
BubbleSort(L);
//------------------選擇排序---------------------
for (i = 0; i < 10; i++)
L->r[i + 1] = arr[i];
printf("簡單選擇排序:\n");
SelectSort(L);
//------------------直接插入排序---------------------
for (i = 0; i < 10; i++)
L->r[i + 1] = arr[i];
printf("直接插入排序:\n");
InsertSort(L);
//------------------希爾排序---------------------
for (i = 0; i < 10; i++)
L->r[i + 1] = arr[i];
printf("希爾排序:\n");
ShellSort(L);
//------------------希爾排序---------------------
for (i = 0; i < 10; i++)
L->r[i + 1] = arr[i];
printf("堆排序:\n");
HeapSort(L);
return 0;
}
1 . 冒泡排序
冒泡排序一種交換排序,它的基本思想是:兩兩比較相鄰記錄的關鍵字,如果反序則交換,知道沒有反序爲止。可以簡單記憶爲,大泡下降,小泡上升。
2. 選擇排序
簡單選擇排序法是通過n-i次關鍵字間的比較,從n-i+1個記錄中選出關鍵字最小的記錄,並和第i(1<=i<=n)個記錄交換。
3. 直接插入排序
3.1 直接插入排序
直接插入排序的基本操作是將一個記錄插入到已經排好序的有序表中,從而得到一個新的、記錄數加1的有序表。(如果覺得理解起來有困難的話建立,摸一手牌,跟着代碼走一遍你就再也不會忘記了(๑╹◡╹)ノ""")
3.2 希爾排序
我們在前一節將的直接插入排序,應該說它的效率在某些時候是很高的,比如,我們的記錄本身就是基本有序的;還有就是記錄數比較少時,直接插入的優勢也是很明顯的。可問題就在於這兩個條件本身就過於苛刻,現實中記錄少或者基本有序都屬於特殊的情況。
不過別急,有條件當然是好,條件不存在嘛,我們創造條件也是可以去做的。於是就有了希爾排序。
希爾排序 = 跳躍分割 + 直接插入排序
4. 堆排序
首先介紹一下堆:
堆是具有下列性質的完全二叉樹:每個節點的值都大於或者等於其左右孩子結點的值,成爲大頂堆;或者每個結點的值都小於或者等於其左右孩子結點的值,稱爲小頂堆。
堆排序算法:將待排序的序列構造成一個大頂堆。此時,整個序列的最大值就是堆頂的根節點。將它移走(其實就是將其與堆數組的末尾元素交換,此時末尾元素就是最大值),然後將剩餘的n-1個序列重新構造成一個堆,這樣就會得到n個元素的次大值。如此反覆執行,便能夠得到一個有序序列了。