学习排序算法时的总结和记录
各种排序算法比较
排序
时间复杂度
空间复杂度
稳定性
直接插入排序
O(n^2)
O(1)
稳定
希尔排序
根据增量序列的不同时间复杂度不同;可以很接近O(nlogn)
O(1)
不稳定
冒泡排序
O(n^2)
O(1)
稳定
快速排序
期望值O(nlogn),需要考虑数组几乎有序和存在大量重复元素的情况
O(1)
不稳定
归并排序
O(nlogn)
O(n)
不稳定
堆排序
O(nlogn)
O(n)
不稳定
直接插入排序(InsertSort) 1 2 3 4 5 6 7 8 9 10 11 12 template <typename T>void SelectSort (T* arr, int n) { for (int i = 0 ; i < n; i++) { int idx = i; for (int j = i + 1 ; j < n; j++) { if (arr[j] < arr[idx]) { idx = j; } } swap (arr[i], arr[idx]); } }
希尔排序(ShellSort) 普通希尔排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 template <typename T>void ShellSort (T* arr, int n) { for (int gap = n / 2 ; gap > 0 ; gap /= 2 ) { for (int i = gap; i < n; i++) { int j; T tmp = arr[i]; for (j = i; j >= gap && arr[j - gap] > tmp; j -= gap) { arr[j] = arr[j - gap]; } arr[j] = tmp; } } }
优化希尔排序(调整increment sequence) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 template <typename T>void ShellSort (T arr[], int n) { int h = 1 ; while (h < n / 3 ) h = 3 * h + 1 ; while (h >= 1 ) { for (int i = h; i < n; i++) { T e = arr[i]; int j; for (j = i; j >= h && e < arr[j - h]; j -= h) arr[j] = arr[j - h]; arr[j] = e; } h /= 3 ; } }
冒泡排序(BubbleSot) 1 2 3 4 5 6 7 8 9 10 template <typename T>void BubbleSot (T* arr, int n) { for (int i = 1 ; i < n; i++) { for (int j = 0 ; j < n - i; j++) { if (arr[j] > arr[j + 1 ]) { swap (arr[j], arr[j + 1 ]); } } } }
冒泡排序的优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 template <typename T>void BubbleSot (T *arr, int n) { int flag = n; int j, k; for (int i = 0 ; i < flag; i++) { k = flag; flag = 0 ; for (j = 0 ; j < k; j++) { if (arr[j] > arr[j+1 ]) { flag = j; swap (arr[j], arr[j+1 ]); } } } }
快速排序(QuickSort) 普通快排 快排核心步骤就是partition操作,partition操作一般情况下就是以arr[l]元素(记为tmp)为基准将arr分为大于tmp和小于tmp两部分 , 最终将其分为 arr[l … j] < tmp和arr[j+1 … r] > tmp两部分
各个变量定义
1 2 3 4 5 6 7 8 9 10 11 12 13 template <typename T>int _Partition(T* arr, int l, int r) { int j = l; T tmp = arr[l]; for (int i = l + 1 ; i <= r; i++) { if (arr[i] < tmp) { swap (arr[j + 1 ], arr[i]); j++; } } swap (arr[j], arr[l]); return j; }
普通快排的缺陷
当数组近乎有序的时候,算法会退化成O(n^2)级别的算法
采用随机选择一个标定元素,则该排序的期望值为O(nlogn),大概率上避免退化成O(n^2);在partition一开始的位置 swap(arr[l], arr[(rand() % (r - l + 1)) + l]);
当存在大量重复元素的时候,算法也会退化成O(n^2)普通快排的优化
将整个数组通过标定元素比较分成三个部分,小于标定,等于标定,大于标定因此如果存在大量重复元素也能高效执行算
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 template <typename T>void _QuickSort3(T* arr, int l, int r) { if (r - l <= 16 ) { InsertSort (arr + l, r - l + 1 ); return ; } swap (arr[l], arr[(rand () % (r - l + 1 )) + l]); int tmp = arr[l]; int lt = l; int gt = r + 1 ; int i = l + 1 ; while (gt > i) { if (arr[i] < tmp) { swap (arr[++lt], arr[i++]); } else if (arr[i] > tmp) { swap (arr[--gt], arr[i]); } else { i++; } } swap (arr[lt], arr[l]); _QuickSort3(arr, l, lt - 1 ); _QuickSort3(arr, gt, r); } template <typename T>void QuickSort3 (T* arr, int n) { srand (time (NULL )); _QuickSort3(arr, 0 , n - 1 ); }
归并排序(MergeSort) Merge操作
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 template <typename T>void _Merge(T* arr, int l, int mid, int r) { T* aux = new T[r - l + 1 ]; for (int i = l; i <= r; i++) { aux[i - l] = arr[i]; } int i = l; int j = mid + 1 ; for (int k = l; k <= r; k++) { if (i > mid) { arr[k] = aux[j - l]; j++; } else if (j > r) { arr[k] = aux[i - l]; i++; } else if (aux[i - l] < aux[j - l]) { arr[k] = aux[i - l]; i++; } else { arr[k] = aux[j - l]; j++; } } delete [] aux; } template <typename T>void _MergeSort(T* arr, int l, int r) { if (r - l <= 15 ) { InsertSort (arr + l, r - l + 1 ); return ; } if (l >= r) { return ; } int mid = (r + l) / 2 ; _MergeSort(arr, l, mid); _MergeSort(arr, mid + 1 , r); _Merge1(arr, l, mid, r); } template <typename T>void MergeSort (T* arr, int n) { _MergeSort(arr, 0 , n - 1 ); }