100萬個無序數排序演算法的執行時間比較
阿新 • • 發佈:2019-01-07
- 氣泡排序(時間複雜度N2,空間複雜度1)
依次比較陣列相鄰兩個元素,將最大或最小元素放到後面,慢慢“浮”到數列的最後。 - 選擇排序(時間複雜度N2,空間複雜度1)
首先找到陣列最小元素,將它和陣列的第一個元素交換位置。再次,在剩下的元素中找到最小的元素,將它與陣列第二個元素交換,如此往復。 - 插入排序(時間複雜度介於N和N2之間,空間複雜度1)
- 將陣列分為有序和無序兩個部分,預設陣列左變第一個元素是有序的,依次遍歷陣列第二個元素到最後,與陣列左變有序序列比較。如果小於左變有序序列則交換位置。
- 希爾排序(時間複雜度NlogN,空間複雜度1)
分組+插入排序。插入排序只能交換相鄰的元素,元素只能一點一點從陣列的一端移動到另一端。希爾排序的思想是使陣列中任意間隔為gap的陣列都是有序的。經驗公式:gap=gap/3+1,如果有12個元素,那麼同一組元素相鄰元素間隔4,下一次分組間隔2,最後一次間隔1。 - 歸併排序(時間複雜度NlogN,空間複雜度N(遞迴臨時變數))
將兩個有序陣列歸併到第三個陣列中,遞迴在發生在處理陣列之後。 - 快速排序(時間複雜度NlogN,空間複雜度lgN))
與歸併一樣,都是利用分治的排序演算法。將陣列分成兩個子陣列,將兩部分獨立排序,遞迴發生在處理陣列之前。不需要額外的記憶體空間儲存一個臨時陣列。 - 堆排序(時間複雜度NlogN,空間複雜度1)
利用堆(一種特殊的完全二叉樹)而設計的一種排序演算法。如果是正序排序,先構建一個最大堆,交換a[1]和a[n],此時a[n]就是陣列中最大元素。n–,a[1]向下調整保持最大堆特性,進行下一次交換。 - 桶排序(時間複雜度lg(M+N),空間複雜度N)
構建一個臨時陣列book,長度為要排序陣列的最大值。遍歷一次陣列a,記錄a[i]的值的次數:book[a[i]]++j.最後遍歷一遍陣列book,將i值和出現的數次寫到陣列a中。
這裡是引用
#include<iostream> #include<time.h> #include <sys/timeb.h> using namespace std; //效率測試 #define NUMBER 1000000 long long getSystemTime() { struct timeb t; ftime(&t); return 1000 * t.time + t.millitm; } void bucketSort(int *a,int *book, int len) { for (int i = 0; i < len; i++) { int tem = a[i]; book[tem]++; } int k = 0; for (int i = 0; i < NUMBER; i++) { int ncount = book[i]; for (int j = 0; j < ncount; j++) { a[k++] = i; } } } //-----------------三向切分的快速排序 --------------------- void quick3WaySort(int *a, int left, int right) { if (left > right) return; int lt = left; int i = left + 1; int gt = right; int tem = a[left]; while (i <= gt) { if (a[i] < tem) //小於切分元素的放在lt左邊,lt和i整體右移 { //exchange(a, lt++, i++); int tmp = a[i]; a[i] = a[lt]; a[lt] = tmp; lt++; i++; } else if (a[i] > tem)//大於切分元素的放在gt右邊,因此gt左移 { //exchange(a, i, gt--); int tmp = a[i]; a[i] = a[gt]; a[gt] = tmp; gt--; } else i++; } quick3WaySort(a,left,lt-1); quick3WaySort(a, gt+1, right); } #if 0 void quickSort(int *a, int left, int right) { if (left > right) return; int i = left; int j = right; int tem = a[left]; while (i != j) { while (a[j]>tem && i<j) j--; while (a[i] <= tem &&i<j) i++; if (i < j) { int t = a[j]; a[j] = a[i]; a[i] = t; } } a[left] = a[i]; a[i] = tem; quickSort(a, left, i - 1); quickSort(a, i+1, right); return; } #endif //---------------堆排序-------------------- void shiftDown(int *a,int len,int i) { int index, flag = 0; //----結點有左孩子----- while (i * 2 <= len && flag == 0) { if (a[i] < a[i * 2]) index = i * 2; else index = i; //----結點有左孩子----- if (i * 2 + 1 <= len) { if (a[index] < a[i * 2 + 1]) index = i * 2 + 1; } if (index != i) { //---交換i節點與子節點,更新編號為下一次向下調整準備 int tem = a[i]; a[i] = a[index]; a[index] = tem; i = index; } else { flag = 1; } } } void heapSort(int *a, int len) { //-------------建最大堆---------------- for (int i = len / 2; i >= 1; i--) { shiftDown(a,len,i); } //--------------排序------------------- while (len>1) { int tem = a[len]; a[len] = a[1]; a[1] = tem; len--; shiftDown(a,len,1); } } //-------------------歸併排序------------------------- void merge(int *a, int first, int mid, int last, int *temp) { int i = first; // 第1個有序序列的開始下標 int j = mid + 1; // 第2個有序序列的開始下標 int len = 0; // 合併兩個有序序列 while (i <= mid && j <= last) { if (a[i] < a[j]) { // 找二者中比較小的數 temp[len] = a[i]; i++; } else { temp[len] = a[j]; j++; } len++; } while (i <= mid) { temp[len] = a[i]; i++; len++; } while (j <= last) { temp[len] = a[j]; j++; len++; } // 找到原來的第一個有序序列的開始位置覆蓋無序序列 for (int k = 0; k < len; k++) { a[first+k] = temp[k]; } } void mergeSort(int *a, int first, int last, int *temp) { if (first == last) return; int mid = first+ (last - first) / 2; //防止first+last溢位 mergeSort(a, first, mid, temp); mergeSort(a, mid+1, last, temp); merge(a, first, mid, last, temp); } //-------------------希爾排序------------------- void shellSort(int *a, int len) { int gap = len; int index, tem; while (gap > 1) { gap = gap / 3 + 1; // =======分組, 對每一組, 進行插入排序 for (int i = 0; i < gap; i++) { //----插入排序---------- for (int j = i + gap; j < len; j += gap) { index=j; tem=a[j]; for (int k = j; k > i; k -= gap) { if (tem < a[k - gap]) { a[k] = a[k - gap]; index = k - gap; } else { break; } } a[index] = tem; } } } } //--------------------插入排序--------------------- void insertSort(int *a, int len) { int index; int tem; // 遍歷無序序列 for (int i = 1; i < len; i++) { index = i; //儲存當前基準數位置 tem = a[i]; // 遍歷有序序列 for (int j = i; j >0; j--) { if (tem < a[j - 1]) //找到比基準數大的位置,則將有序序列後移,並記錄有序序列位置 { a[j] = a[j - 1]; index = j - 1; } else { break; } } a[index] = tem; //將基準數填入 } } //--------------選擇排序--------------- void selectSort(int *a, int len) { for (int i = 0; i < len; i++) { int min = i;//基準書位置 for (int j = i + 1; j < len; j++) { if (a[min] > a[j]) min = j; //沒遍歷一次,找到最小值的序號 } int tem = a[min]; //交換基準數和最小元素 a[min] = a[i]; a[i] = tem; } } //----------------氣泡排序------------------ void bubbleSort(int *a, int len) { int flag = 0;//0表示沒有排序好,1表示排序好了 for (int i = 0; i < len && flag == 0; i++) { //每進行一次冒泡,首先預設已經排好 flag = 1; for (int j = 1; j < len - i; j++) { if (a[j] < a[j - 1]) { int tem = a[j - 1]; a[j - 1] = a[j]; a[j] = tem; flag = 0; } } } } int main() { long long timeStart, timeEnd, timeUse; srand((unsigned)time(NULL)); int(*array)[NUMBER] = new int[7][NUMBER]; int *arrayHeap = new int[NUMBER+1]; //-----歸併和桶排序額外的記憶體--- int *mergeArray = new int[NUMBER]; int *bucketArrayBook = new int[NUMBER](); for (int i = 0; i < NUMBER; ++i) { //生成一個小於len的隨機數 int number = rand() % NUMBER; for (int j = 0; j < 7; ++j) { array[j][i] = number; } arrayHeap[i+1] = number; } cout << "對第" << NUMBER << "個數進行排序" << endl; timeStart = getSystemTime(); bucketSort(array[0], bucketArrayBook, NUMBER); timeEnd = getSystemTime(); timeUse = timeEnd - timeStart; cout << "bucketSort 耗時:" << timeUse << "ms" << endl;; timeStart = getSystemTime(); quick3WaySort(array[1], 0, NUMBER-1); timeEnd = getSystemTime(); timeUse = timeEnd - timeStart; cout << "quick3WaySort 耗時:" << timeUse << "ms" << endl;; timeStart = getSystemTime(); heapSort(arrayHeap, NUMBER); timeEnd = getSystemTime(); timeUse = timeEnd - timeStart; cout << "heapSort 耗時:" << timeUse << "ms" << endl;; timeStart = getSystemTime(); mergeSort(array[2],0, NUMBER-1, mergeArray); timeEnd = getSystemTime(); timeUse = timeEnd - timeStart; cout << "mergeSort 耗時:" << timeUse << "ms" << endl;; timeStart = getSystemTime(); shellSort(array[3], NUMBER); timeEnd = getSystemTime(); timeUse = timeEnd - timeStart; cout << "shellSort 耗時:" << timeUse << "ms" << endl;; timeStart = getSystemTime(); insertSort(array[4], NUMBER); timeEnd = getSystemTime(); timeUse = timeEnd - timeStart; cout << "insertSort 耗時:" << timeUse << "ms" << endl;; timeStart = getSystemTime(); selectSort(array[5], NUMBER); timeEnd = getSystemTime(); timeUse = timeEnd - timeStart; cout << "selectSort 耗時:" << timeUse << "ms" << endl;; timeStart = getSystemTime(); bubbleSort(array[6], NUMBER); timeEnd = getSystemTime(); timeUse = timeEnd - timeStart; cout << "bubbleSort 耗時:" << timeUse << "ms" << endl; delete []array; delete arrayHeap; delete mergeArray; delete bucketArrayBook; }
總結:
1.在這8中排序演算法當中,桶排序是最快的,氣泡排序最慢。
但是,桶排序要求構建一個待排序最大元素的陣列,往往排序的時候我們並不能確定最大元素的值,另一方面和並歸排序一樣,當陣列很大的時候,也要考慮額外的記憶體分配問題。
2.在陣列非常長的排序,各種排序演算法中,三向切分快速排序是最快的(較快速排序提升了20%到30%)。但是,需要考慮遞迴產生臨時是否會導致棧溢位的問題。堆排序時間複雜度ONlogN,空間複雜度1,並且對已經排序好的陣列插入一個元素的時候,堆是一種優先佇列的時間,查詢和插入的時間複雜度較優。
3.對於小陣列,快速排序比插入排序慢。因此在設計排序的時候可以考慮通過判斷長度來切換排序演算法。