排序算法是最基本且重要的一類算法,本文基於 VS2017,使用 C 語言來實現一些基本的排序算法。 算法
1、選擇排序shell
選擇排序,先找到數組中最小的元素,而後將這個元素與數組的第一個元素位置互換(若是第一個元素就是最小元素,則與本身互換位置)。而後在剩下的元素中尋找最小的元素,與第二個元素位置互換。以此循環,直到整個數組完成排序。數組
算法描述:數據結構
1)第一趟,從無序的數組中選出最小的元素,與第一個元素交換位置;ui
2)第二趟,除去第一個元素,剩下的元素組成一個無序數組,從中找出最小的元素而後與第二個元素交換位置;spa
3)重複 n - 1 次便可獲得有序的數組。指針
算法分析:code
選擇排序的時間複雜度爲 O(N2),空間複雜度爲 O(1),是非穩定排序blog
#include <stdio.h> #include <stdlib.h> #include <string.h>
int *selectSort(int *numArray,int arrayLen); int main() { int numArray[] = {10,5,21,3,7,0,11,59,12,11,20}; int arrayLen = sizeof(numArray) / sizeof(int); int *newArray = selectSort(numArray,arrayLen); for (int k = 0; k < arrayLen; k++) { printf("%d ",newArray[k]); } return 0; } int *selectSort(int *numArray,int arrayLen) { int len = arrayLen; int temp = 0; for (int i = 0; i < len - 1; i++) { for (int j = i + 1; j < len; j++) { if (numArray[j] <= numArray[i]) { temp = numArray[i]; numArray[i] = numArray[j]; numArray[j] = temp; } } } return numArray; }
2、直接插入排序排序
直接插入排序將一個數組分爲一個有序數組和一個無序數組。一開始,第一個元素單獨做爲一個有序數組,其餘元素爲無序數組;而後,每次從無序數組中取出第一個元素,與有序數組中的元素進行比較並插入到有序數組中去,與之組成一個新的有序數組,以此重複直到排序完成。
算法描述:
1)第一個元素本身做爲一個有序數組;
2)從第二個元素開始,將它與其左側第一個元素比較,若左側第一個元素比它大,則繼續與左側第二個元素比較,直到遇到不大於(小於或等於)該元素的元素,將該元素插入到所找到的元素的右邊,此時該元素及其左邊的元素是已排序的;
3)選取第 三、四、...、n 個元素,重複步驟 2;
4)獲得有序的數組。
算法分析:
直接插入排序的時間複雜度爲 O(N2),空間複雜度爲 O(1),是穩定排序
#include <stdio.h> #include <stdlib.h> #include <string.h> int *insertSort(int *numArray,int arrayLen); int main() { int len; int numArray[] = {10,23,1,6,4,20,45,5,3,7,19,100,22}; len = sizeof(numArray) / sizeof(int); int *newArray = insertSort(numArray,len); for (int k = 0; k < len; k++) { printf("%d ",newArray[k]); } return 0; } int *insertSort(int *numArray,int arrayLen) { int temp; for (int i = 1; i < arrayLen; i++) { for (int j = i; j > 0; j--) { if (numArray[j] < numArray[j - 1]) { temp = numArray[j]; numArray[j] = numArray[j - 1]; numArray[j - 1] = temp; } } } return numArray; }
3、冒泡排序
冒泡排序一次比較兩個元素,若是前一個元素比後一個元素大,則交換兩個元素的位置。從第一個元素開始,對每對相鄰的元素都執行這個操做, 直到最後一個元素,則一次比較完成後,最大的元素會被放到數組最右邊;去掉最右邊的元素再對剩餘的數組進行冒泡排序,直到全部元素都完成排序。
算法描述:
1)從第一個元素開始,比較兩個相鄰的元素(第一、2個元素),若後者比前者小,則交換二者的位置;
2)依次交換全部的元素,一趟交換完成後,最右邊的元素爲最大的元素;
3)而後再從第一個元素開始交換,除了最後一個元素;
4)重複,知道全部元素都被排序。
算法分析:
冒泡排序的時間複雜度爲 O(N2),空間複雜度爲 O(1),是穩定排序
#include <stdio.h> #include <stdlib.h> #include <string.h> int *bubbleSort(int *numArray,int arrayLen); int main() { int len; int numArray[] = {20,13,2,9,5,34,56,25,18,0,33,67}; len = sizeof(numArray) / sizeof(int); int *newArray = bubbleSort(numArray,len); for (int k = 0; k < len; k++) { printf("%d ", newArray[k]); } return 0; } int *bubbleSort(int *numArray, int arrayLen) { int temp; if (arrayLen < 2) { return numArray; } for (int i = 0; i < arrayLen; i++) { for (int j = 0; j < arrayLen - i - 1; j++) { if (numArray[j + 1] < numArray[j]) { temp = numArray[j + 1]; numArray[j + 1] = numArray[j]; numArray[j] = temp; } } } return numArray; }
4、希爾排序
希爾排序是第一批突破 O(n2) 時間複雜度的算法之一,它是插入排序的一種變種,與插入排序不一樣的是,它經過比較相距必定間隔的元素來工做;各趟比較所用的距離隨着算法的進行而減少,直到最後一趟比較(只比較相鄰元素)爲止,這是距離爲 1 。所以希爾排序又叫縮小增量排序。
算法描述:
1)選擇一個增量 k(通常爲數組長度的一半),以這個增量爲步長將數組分爲 k 個小數組,對每一個小數組進行直接插入排序;
2)縮小增量爲 k / 2,以這個增量爲步長將數組分爲 k / 2 個小數組,對每一個小數組進行直接插入排序,因爲這個小數組有一部分是已經排序了的,所以排序起來會比較容易;
3)繼續縮小增量,並排序,直到增量爲 1;
4)最終獲得一個已排序的數組。
算法分析:
希爾排序的時間複雜度爲 O(N log N),空間複雜度爲 O(1),爲非穩定排序。
#include <stdio.h> #include <stdlib.h> #include <string.h> int *shellSort(int *numArray, int arrayLen); int main() { int len; int numArray[] = {13,2,8,18,30,22,0,81,45,36,7,65,22}; len = sizeof(numArray) / sizeof(int); int *newArray = shellSort(numArray,len); for (int k = 0; k < len; k++) { printf("%d ", newArray[k]); }
return 0; } int *shellSort(int *numArray,int arrayLen) { int step; int temp; int j; if (arrayLen < 2) return numArray; for (step = arrayLen / 2; step > 0; step /= 2) { for (int i = step; i < arrayLen; i++) { temp = numArray[i]; for (j = i - step; j >= 0 && temp < numArray[j]; j -= step) { numArray[j + step] = numArray[j]; } numArray[j + step] = temp; } } return numArray; }
5、歸併排序
歸併排序的基本思想是先把一個大的無序數組拆分紅兩個小的無序數組,而後對這兩個數組分別進行排序,以後再將兩個有序的小數組合併成一個有序的大數組。
經過遞歸的方式,將數組一直分割,直到數組大小爲 1 ,此時數組只有一個元素,爲有序狀態;而後將兩個大小爲 1 的數組合併成一個大小爲 2 的數組;再把大小爲 2 的兩個數組合併成大小爲 4 的數組...直到將全部的數組合併成一個大數組。
算法描述:
1)將一個無序的數組分紅兩個無序的小數組;
2)將兩個無序的小數組分別分紅兩個更小的無序數組;
3)重複第二步,知道小數組的長度爲1,此時每一個小數組都是有序的;
4)將長度爲 1 的兩個小數組合併成一個長度爲 2 的有序數組;
5)將長度爲 2 的兩個小數組合併成長度爲 4 的有序數組;
6)最終合成一個有序的大數組。
很顯然,用遞歸的思想實現歸併排序會比較方便。
算法分析:
歸併排序的時間複雜度爲 O(N log N),空間複雜度爲 O(N),爲穩定排序。
#include <stdio.h> #include <stdlib.h> #include <string.h> void mergeSort(int *sourceArray,int arrayLen); void mSort(int *sourceArray, int *tmpArray, int left, int right); void merge(int *sourceArray, int *tmpArray, int lpos, int rpos, int rightEnd); int main() { int sourceArray[] = {27,23,98,10,44,26,4,9,2,0,11,56,34,67,10}; int len = sizeof(sourceArray) / sizeof(int); mergeSort(sourceArray,len); for (int k = 0; k < len; k++) { printf("%d ", sourceArray[k]); }
return 0; } // 歸併排序 void mergeSort(int *sourceArray, int arrayLen) { int *tmpArray = (int *)malloc(sizeof(int) * arrayLen); // 新建一個臨時數組 if (tmpArray != NULL) { mSort(sourceArray, tmpArray, 0, arrayLen - 1); free(tmpArray); } else { perror("malloc failed!"); exit(EXIT_FAILURE); } } // 排序 void mSort(int *sourceArray, int *tmpArray, int left, int right) { int mid = (left + right) / 2; if (left < right) { mSort(sourceArray, tmpArray, left, mid); mSort(sourceArray, tmpArray, mid + 1, right); merge(sourceArray, tmpArray, left, mid + 1, right); } } // 歸併 void merge(int *sourceArray,int *tmpArray, int lpos, int rpos, int rightEnd) { int i, leftEnd, elementNum, tmpPos; tmpPos = lpos; // 臨時指針 leftEnd = rpos - 1; elementNum = rightEnd - lpos + 1; // 歸併比較 while (lpos <= leftEnd && rpos <= rightEnd) { if (sourceArray[lpos] <= sourceArray[rpos]) { tmpArray[tmpPos++] = sourceArray[lpos++]; } else { tmpArray[tmpPos++] = sourceArray[rpos++]; } } // 將左邊剩餘元素填入數組 while (lpos <= leftEnd) { tmpArray[tmpPos++] = sourceArray[lpos++]; } // 將右邊剩餘元素填入數組 while (rpos <= rightEnd) { tmpArray[tmpPos++] = sourceArray[rpos++]; } // 將臨時數組中的元素拷貝到原數組中去 for (i = 0; i < elementNum; i++, rightEnd--) { sourceArray[rightEnd] = tmpArray[rightEnd]; } }
6、快速排序
快速排序是在實踐中已知最快的一種排序算法。其基本思想是先從數組中選擇一個元素做爲基準元素,而後分別從數組的頭部和尾部開始往數組中間遍歷,並與基準元素比較,一次遍歷的結果是將這個基準元素移到數組的中間(基準數的左邊全部元素都小於或等於它,右邊全部元素都大於或等於它);而後再從這個基準元素的左邊和右邊各選一個元素做爲兩邊小數組各自的基準元素,重複以前的作法,將基準元素移到數組中間,直到全部元素都被排好序。
算法描述:
1)選擇一個基準元素(好比說數組最左邊的元素);
2)設置兩個哨兵(指針) left 和 right,分別指向數組最左邊的元素和數組最右邊的元素;
3)先移動 right 哨兵,從數組右邊往左邊移動,當 right 哨兵所指位置的元素小於基準數時,中止移動 right 哨兵;
4)再移動 left 哨兵,從數組左邊往右邊移動,當 left 哨兵所指位置的元素大於基準數時,中止移動 left 哨兵;
5)交換 left 和 right 所指位置的元素;
6)重複步驟 3~5,直到 left >= right,中止移動,並將基準元素與 left 所指向位置的元素互換;
7)此時基準元素左邊數組的元素所有小於它,右邊數組的元素所有大於它;
8)對基準元素左邊和右邊的數組分別重複步驟 2~6;
9)獲得被排序的數組。
算法分析:
快速排序的時間複雜度爲 O(N log N),空間複雜度爲 O(log N),爲非穩定排序。
#include <stdio.h> #include <stdlib.h> #include <string.h> void quickSort(int *sourceArray, int leftPos, int rightPos); int main() { int sourceArray[] = { 29,13,4,23,10,54,27,87,33,0,32,5,90,76 }; int len = sizeof(sourceArray) / sizeof(int); quickSort(sourceArray, 0, len - 1); for (int k = 0; k < len; k++) { printf("%d ", sourceArray[k]); }
return 0; } void quickSort(int *sourceArray, int leftPos, int rightPos) { int baseNum = sourceArray[leftPos]; // 基準數 int tmpPosLeft = leftPos; // tmpPos 用於存放基準數在數組中的位置 int tmpPosRight = rightPos; int temp; if (leftPos >= rightPos) { return; } while (leftPos < rightPos) { // 先從右邊找起,找到比基準數小的數爲止 while (leftPos < rightPos && sourceArray[rightPos] >= baseNum) { rightPos--; } // 再從左邊找,找到比基準數大的爲止 while (leftPos < rightPos && sourceArray[leftPos] <= baseNum) { leftPos++; } // 交換兩個哨兵所在處的值 if (leftPos < rightPos) { temp = sourceArray[leftPos]; sourceArray[leftPos] = sourceArray[rightPos]; sourceArray[rightPos] = temp; } } // 最後將基準數放到數組中央,此時基準數左邊的數所有小於它,基準數右邊的數所有大於它 sourceArray[tmpPosLeft] = sourceArray[leftPos]; sourceArray[leftPos] = baseNum; quickSort(sourceArray, tmpPosLeft, leftPos - 1); quickSort(sourceArray, leftPos + 1, tmpPosRight); }
參考資料:
《數據結構與算法分析 -- C語言描述》