大纲
复杂度
排序名 | 最优时间复杂度 | 最坏时间复杂度 | 平均时间复杂度 | 空间复杂度 | 稳定性 | 复杂性 |
---|---|---|---|---|---|---|
插入排序 | O(n) | O(n2) | O(n2) | O(1) | 稳定 | 简单 |
冒泡排序 | O(n) | O(n2) | O(n2) | O(1) | 稳定 | 简单 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 | 较复杂 |
计数排序 | O(n ) | O(n + K) | O(n + k) | O(K) | 稳定 | 简单 |
桶排序 | O(n) | O(n2) | O(n + C) | O(n + m) | 稳定 | 较复杂 |
选择排序 | O(n2) | O(n2) | O(n2) | O(1) | 不稳定 | 简单 |
快速排序 | O(nlogn) | O(n2) | O(nlogn) | O(nlogn) | 不稳定 | 较复杂 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 | 较复杂 |
希尔排序 | O(n) | O(n2) | O(nlogn) | O(1) | 不稳定 | 较复杂 |
注:C=N*(logN-logM), n个数据,m个桶
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n)
稳定排序:
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
我都可以代码实现询问面试官哪个算法需要展开?
计数排序
#include<iostream>
using namespace std;
const int MAX_N = 100000;
int nums[MAX_N + 5];
int temp[MAX_N + 5];
int res[MAX_N + 5];
void CourtSort(int *nums, int *temp, int k, int n) {
for (int i = 1; i <= k; ++i) {
temp[i] += temp[i - 1];
}
for (int i = n - 1; i >= 0; --i) {
res[--temp[nums[i]]] = nums[i];
}
return ;
}
int main() {
int n;
cin >> n;
int k = 0;
for (int i = 0; i < n; ++i) {
cin >> nums[i];
k = max(k, nums[i]);
++temp[nums[i]];
}
CourtSort(nums, temp, k, n);
for (int i = 0; i < n; ++i) {
cout << res[i] << endl;
}
return 0;
}
归并排序
#define swap(a, b) {\
__typeof(a) __temp = (a);\
(a) = (b), (b) = __temp;\
}
void merge_sort(int *data, int l, int r) {
if(r - l <= 1) {
if (r - l == 1 && data[l] > data[r]) {
swap(data[l], data[r]);
}
return ;
}
int mid = (l + r) >> 1;
merge_sort(data, l, mid);
merge_sort(data, mid + 1, r);
int *temp = (int *)malloc(sizeof(int) * (r - l + 1));
int x = l, y = mid + 1, loc = 0;
while (x <= mid || y <= r) {
if (x <= mid && (y > r || data[x] <= data[y])) {
temp[loc++] = data[x++];
} else {
temp[loc++] = data[y++];
}
}
memcpy(data + l, temp, sizeof(int) * (r - l + 1));
free(temp);}
插入排序
#define swap(a, b) {\
__typeof(a) __temp = (a);\
(a) = (b), (b) = __temp;\
}
void insert_sort(int *data, int n) {
for (int i = 1; i < n; i++) {
for (int j = i; j > 0 && (data[j] < data[j - 1]); j--) {
swap(data[j], data[j - 1]);
}
}
return ;
}
冒泡排序
#define swap(a, b) {\
__typeof(a) __temp = (a);\
(a) = (b), (b) = __temp;\
}
void bubble_sort (int *data, int n) {
int times = 1;
for (int i = n - 1; i >= 1 && times; i--) {
times = 0;
for (int j = 0; j < i; j++) {
if (data[j] <= data[j + 1]) continue;
swap(data[j], data[j + 1]);
times += 1;
}
}
return ;
}
选择排序
#define swap(a, b) {\
__typeof(a) __temp = (a);\
(a) = (b), (b) = __temp;\
}
void select_sort(int *num, int n) {
for (int i = 0; i < n - 1; i++) {
int ind = i;
for (int j = i + 1; j < n; j++) {
if (num[ind] > num[j]) ind = j;
}
swap(num[i], num[ind]);
}
return ;
}
快排
3、快排最差情况推倒
在快速排序的早期版本中呢,最左面或者是最右面的那个元素被选为枢轴,那最坏的情况就会在下面的情况下发生啦:
1)数组已经是正序排过序的。 (每次最右边的那个元素被选为枢轴)
2)数组已经是倒序排过序的。 (每次最左边的那个元素被选为枢轴)
3)所有的元素都相同(1、2的特殊情况)
因为这些案例在用例中十分常见,所以这个问题可以通过要么选择一个随机的枢轴,或者选择一个分区中间的下标作为枢轴,或者(特别是对于相比更长的分区)选择分区的第一个、中间、最后一个元素的中值作为枢轴。有了这些修改,那快排的最差的情况就不那么容易出现了,但是如果输入的数组最大(或者最小元素)被选为枢轴,那最坏的情况就又来了。(个人觉得还是"三值取中"法最好)
快速排序,在最坏情况退化为冒泡排序,需要比较O(n2)次(n(n - 1)/2次)。
#define swap(a, b) {\
__typeof(a) __temp = (a);\
(a) = (b), (b) = __temp;\
}
//未优化
void quick_sort(int *num, int l, int r) {
if (l > r) return ;
int x = l, y = r, z = num[l];
while(x < y) {
while(x < y && num[y] >= z) y--;
if (x < y) num[x++] = num[y];
while (x < y && num[x] <= z) x++;
if (x < y) num[y--] = num[x];
}
num[x] = z;
quick_sort(num, l, x - 1);
quick_sort(num, x + 1, r);
return ;
}
//优化
inline int middle(int *arr, int l, int r, int mid) {
int a = arr[l], b = arr[r], c = arr[mid];
if (a > b) swap(a, b);
if (a > c) swap(a, c);
if (b > c) swap(b, c);
return b;
}
void quick_sort(int *num, int l, int r) {
while (l < r) {
int x = l, y = r, z = (l + r) >> 1;
int mid = middle(num, x, y, z);
do {
while (x <= y && num[x] < mid) x++;
while (x <= y && num[y] > mid) y--;
if (x <= y) {
swap(num[x], num[y]);
x++, y--;
}
} while(x <= y);
quick_sort(num, x, r);
r = y;
}
return ;
}
堆排序
#define swap(a, b) {\
__typeof(a) __temp = (a);\
(a) = (b), (b) = __temp;\
}
void downUpdata(int *arr, int ind, int n) {
while ((ind << 1) <= n) {
int temp = ind, l = ind << 1, r = ind << 1 | 1;
if (l <= n && arr[l] > arr[temp]) temp = l;
if (r <= n && arr[r] > arr[temp]) temp = r;
if (temp == ind) break;
swap(arr[temp], arr[ind]);
ind = temp;
}
return ;
}
void heap_sort(int *arr, int n) {
arr -= 1;
for (int i = n >> 1; i >= 1; i--) {
downUpdata(arr, i, n);
}
for (int i = n; i > 1; i--) {
swap(arr[i], arr[1]);
downUpdata(arr, 1, i - 1);
}
return ;
}
希尔排序
template<typename T>
void insert_sort(T st, T ed, T delta, T *nums) {
for (T i = st + delta; i < ed; i += delta) {
for (T j = i; j > st; j -= delta ) {
if (nums[j] < nums[j - delta]) {
swap(nums[j], nums[j - delta]);
} else {
break;
}
}
}
}
template<typename T>
void shell_sort(T *nums, T st, T ed) {
for (int delta = (ed - st) / 2; delta; delta /= 2) {
for (int i = 0; i < delta ; i++) {
insert_sort(st + i, ed, delta, nums);
}
}
}