针对前面讨论的八大经典排序算法:冒泡排序、插入排序、选择排序、堆排序、归并排序、快速排序、希尔排序、桶排序。
关于各种什么时间复杂度,空间复杂度的对比总结,网上一大堆,别人也总结的很好,这里就不赘述了,这里主要通过对大量数据进行排序测试,测试各排序算法的运行时间,来比较各排序之间优劣(时间上)。
先贴出测试程序:最后会给出各数据量的情况下,各算法的运行时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| typedef void(*SortFun)(int Arr[], int length);
double SortTest(SortFun fp) { int Arr[LENGTH]; for (int i = 0; i < LENGTH; ++i) Arr[i] = rand() % MAXNUM; clock_t beginTime = clock(); fp(Arr, LENGTH); double diffTime = double(clock() - beginTime) / CLOCKS_PER_SEC;
return diffTime;
}
|
上面的 LENGTH 和 MAXNUM 宏定义在另外一个头文件中(因为桶排序中需要用到 MAXNUM),LENGTH 为待排数据的量,MAXNUM 为待排数据数值的最大值。
为了方便调用函数指针,这里在原排序程序的基础上添加了一个通用的函数接口。为方便理解,这里重新贴出各排序程序
1、选择排序
思想:每次找一个最小值
特点:不稳定排序,in-place sort
最坏时间情况:O(N^2)
最好时间情况:O(N^2)
空间复杂度:O(1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void SelectSort(int Array[], int length) { int i, j; int Minindex;
for (i = 0; i < length - 1; ++i) { Minindex = i; for (j = i + 1; j < length; ++j) { if (Array[j] < Array[Minindex]) Minindex = j; } swap(Array[i], Array[Minindex]); }
}
|
2、桶排序
思想:类似于哈希表的分离链表法,定义一个映射函数,将关键字划入对应的桶中
特点:稳定排序
最坏时间情况:全部分到一个桶中O(N^2),一般情况为O(NlogN)
最好时间情况:每个桶中只有一个数据时最优O(N)
空间复杂度:典型地以空间换时间,和哈希表类似,看要多少桶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| typedef struct Node { int data; struct Node *next; }node;
void AddtoBucket(node *&pNode, int value) { node *pHead = NULL; int tempValue;
node *pNew = new node; pNew->data = value; pNew->next = NULL; if (NULL == pNode) { pNode = pNew; } else { pHead = pNode; while ((pNode->data <= value) && (pNode->next != NULL)) pNode = pNode->next; if (pNode->data <= value) { pNode->next = pNew; } else { tempValue = pNode->data; pNode->data = pNew->data; pNew->data = tempValue; pNew->next = pNode->next; pNode->next = pNew; } pNode = pHead; }
}
void BucketSort(int Arr[], int length, int MaxNum) { node **list = new node*[length]; for (int i = 0; i < length; ++i) list[i] = NULL;
int index; for (int i = 0; i < length; ++i) { index = (Arr[i] * length) / (MaxNum + 1); AddtoBucket(list[index], Arr[i]); } for (int i = 0, j = 0; i < length; ++i) { while (list[i] != NULL) { Arr[j++] = list[i]->data; list[i] = list[i]->next; } }
}
void BucketSortTest(int Arr[], int length) { BucketSort(Arr, length, MAXNUM); } 3、
|
希尔排序
思想:分割成子序列,然后用直接插入排序
特点:不稳定排序算法
平均时间复杂度:O()
空间复杂度:O(1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void ShellSort(int Array[], int length) { int i, j, gap; int temp;
for (gap = length / 2; gap > 0; gap /= 2) for (i = 0; i < gap; ++i) { //下面为直接插入排序,排序的是数组里间隔gap的元素 for (j = i + gap; j < length; j += gap) { int k = j - gap; temp = Array[j]; while (k >= 0 && Array[k] > temp) { Array[k + gap] = Array[k]; k -= gap; } Array[k + gap] = temp; } }
}
|
4、插入排序
思想:将无序区的元素比较插入到有序区适当位置,是移动不是交换,所以是稳定排序算法
特点:稳定排序算法,in-place sort
时间复杂度:最坏O(N^2),最优O(N)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void InsertSort(int Array[], int length) { int i, j; int temp;
for (i = 1; i < length; ++i) { j = i - 1; temp = Array[i]; //待插入数据,即无序区的第一个元素 while ((j >= 0) && (temp < Array[j])) //跳出循环的两个条件 { Array[j + 1] = Array[j]; //数据后移 } Array[j + 1] = temp; }
}
|
5、归并排序
思想:分治法
特点:稳定排序算法
时间复杂度:O(NlogN)
空间复杂度:O(N+logN)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| void Merge(int unsorted[], int first, int mid, int last, int sorted[]) { int i = first, j = mid; int k = 0;
while (i < mid && j < last) { if (unsorted[i] < unsorted[j]) sorted[k++] = unsorted[i++]; else sorted[k++] = unsorted[j++]; } while (i < mid) sorted[k++] = unsorted[i++]; while (j < last) sorted[k++] = unsorted[j++];
for (int v = 0; v < k; ++v) unsorted[v + first] = sorted[v];
}
void MergeSort(int unsorted[], int first, int last, int sorted[]) { if (first + 1 < last) { int mid = first + ((last - first) >> 1); MergeSort(unsorted, first, mid, sorted); MergeSort(unsorted, mid, last, sorted); Merge(unsorted, first, mid, last, sorted); } }
void MergeSortTest(int Arr[], int length) { int *sorted = new int[length]; MergeSort(Arr, 0, length, sorted); } 6、
|
堆排序
思想:最大堆(最小堆)
特点:不稳定排序算法
时间复杂度:O(NlogN)
空间复杂度:O(1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #define PARENT(x) ((x-1) >> 1)
#define LEFT(x) ((x << 1) + 1)
#define RIGHT(x) ((x << 1) + 2)
//调整最大堆 void MaxHeapify(int Array[], int Index, int HeapSize) { int L = LEFT(Index); //Index为父节点索引 int R = RIGHT(Index); int Largest;
//选择三(两个)个元素(“血缘关系”)的最大值 if (L <= HeapSize && Array[L] > Array[Index]) Largest = L; else Largest = Index; if (R <= HeapSize && Array[R] > Array[Largest]) Largest = R; if (Largest != Index) { swap(Array[Largest], Array[Index]); //最大值与父节点位置交换 MaxHeapify(Array, Largest, HeapSize);
}
}
//构建最大堆 void BuildHeapify(int Array[], int HeapSize) { for (int i = PARENT(HeapSize); i >= 0; MaxHeapify(Array, i, HeapSize); //必须单步向前 }
void HeapSort(int Array[], int length) { int HeapSize = length - 1;
BuildHeapify(Array, HeapSize); for (int i = HeapSize; i >= 0; { swap(Array[0], Array[i]); MaxHeapify(Array, 0, HeapSize); //删除根节点后,,有一棵子树满足最大堆,有一棵不满足,只需调整一棵子树 }
}
|
7、快速排序
思想:分治,化整为零,与基准比较
特点:不稳定排序算法
时间复杂度:O(NlogN),如果数据本来是排序好的,最坏O(N^2)
空间复杂度:O(logN)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| int Partition(int Array[], int left, int right) { int L = left, R = right; int pivot = Array[L];
while (L < R) { while ((L < R) && (Array[R] > pivot)) --R; if (L < R) { Array[L] = Array[R]; ++L; } while ((L < R) && (Array[L] < pivot)) ++L; if (L < R) { Array[R] = Array[L]; --R; } } Array[L] = pivot; return L;
}
void QuickSort(int Array[], int left, int right) { if (left < right) { int pos = Partition(Array, left, right); QuickSort(Array, left, pos - 1); QuickSort(Array, pos + 1, right); } }
void QuickSortTest(int Arr[], int length) { QuickSort(Arr, 0, length - 1); }
|
8、冒泡排序
思想:冒泡
特点:稳定排序算法
时间复杂度:O(N^2)
1 2 3 4 5 6 7 8 9
| void BubbleSort(int Array[], int length) { for (int i = 0; i < length - 1; ++i) for (int j = 1; j < length - i; ++j) { if (Array[j - 1] > Array[j]) swap(Array[j - 1], Array[j]); } }
|
主程序:
避免长久的等待,将冒泡排序置最后。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int main() { cout << "BucketSort: " << SortTest(BucketSortTest) << endl; cout << "HeapSort : " << SortTest(HeapSort) << endl; cout << "InsertSort: " << SortTest(InsertSort) << endl; cout << "MergeSort : " << SortTest(MergeSortTest) << endl; cout << "QuickSort : " << SortTest(QuickSortTest) << endl; cout << "SelectSort: " << SortTest(SelectSort) << endl; cout << "ShellSort : " << SortTest(ShellSort) << endl; cout << "BubbleSort: " << SortTest(BubbleSort) << endl;
return 0;
}
|
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #define LENGTH 20000
#define MAXNUM 10000
#define LENGTH 10000
#define MAXNUM 10000
#define LENGTH 5000
#define MAXNUM 10000
#define LENGTH 50000
#define MAXNUM 100000
|
这里的冒泡排序需要等很久
上面运行时间的计算,受很多因素的影响,这里只是对比各大排序的排序时间。毫无疑问快速排序是最快的。