快速排序
思想:基于二分思想,划分一个主元。把整个数组通过主元划分成两部分,小于主元的在左边,大于的在右边。对左右部分递归划分即可完成排序
时间复杂度:
时间复杂度在最佳情况是 O ( n l o g n ) O(nlog_n) O(nlogn),但是如果分界点元素选择不当可能会退化到 O ( n 2 ) O(n^2) O(n2),但是这种情况比较少见(比如数组完全逆序),如果随机选择分界点的话,时间复杂度能够稳定在 O ( n l o g n ) O(nlog_n) O(nlogn)。另外如果元素中相同元素数量比较多的话,也会降低排序性能。
空间复杂度在 O ( l o g n ) O(log_n) O(logn)水平,属于堆栈调用,在最坏的情况下空间复杂度还是 O ( n ) O(n) O(n),平均情况下复杂度是 O ( l o g n ) O(log_n) O(logn)
快速排序是不稳定的,因为包含跳跃式交换元素位置。
void quick_sort(int nums[], int left, int right) {
if (left >= right) return ;
int low = left + 1, high = right;
while (low <= high) {
if (nums[low] < nums[left]) low++;
else swap(nums[low], nums[high--]); // 交换两个元素值
}
swap(nums[left], nums[--low]);
quick_sort(nums, left, low - 1);
quick_sort(nums, low + 1, right);
}
冒泡排序
思想:像泡泡一样,左右互相比较,把最大的往右移动
void bubble_sort(int nums[], int length) {
for (int i = length - 1; i >= 1; i--) { // 最大有序区间
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
swap(nums[j], nums[j + 1]);
}
}
}
}
选择排序
思想:从无序区里选择一个最大的数或者最小的数**交换(因此这个基于数组的算法不稳定)**到有序区间的一端,在无序区选某个数
// 选择排序 选择n个元素的最大值和最后一个元素交换
void select_sort(int nums[], int length) {
for (int i = length - 1; i >= 1; i--) {
int max_index = 0;
for (int j = 0; j <= i; j++) {
if (nums[j] > nums[max_index]) {
max_index = j;
}
}
if (nums[max_index] > nums[i]) {
swap(nums[max_index], nums[i]);
}
}
}
插入排序
思想:从无序区间的第一个数插入到有序区间里,在有序区间里面做排序
优化:二分插入
// 插入排序 最小的维护在左区间
void insert_sort(int nums[], int length) {
for (int i = 1; i < length; i++) {
int j = i;
while (j > 0 && nums[j] < nums[j - 1]) {
swap(nums[j], nums[j - 1]);
j--;
}
}
}
归并排序
void merge_sort(int nums[], int left, int right) {
if (left >= right) return ;
int mid = (left + right) / 2; // 从中间进行划分
merge_sort(nums, left, mid);
merge_sort(nums, mid + 1, right);
int temp[right - left + 1]; // 临时数组
int i = left, j = mid + 1;
int index = 0;
while (i <= mid && j <= right) {
if (nums[i] <= nums[j]) temp[index++] = nums[i++];
else temp[index++] = nums[j++];
}
while (i <= mid) temp[index++] = nums[i++];
while (j <= right) temp[index++] = nums[j++];
// 拷贝回原来的数组
for (int i = 0; i < index; i++) {
nums[left + i] = temp[i];
}
}
总结
排序算法 | 最好情况 | 平均情况 | 最差情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | n 2 n^2 n2 | n 2 n^2 n2 | n 2 n^2 n2 | 1 | ✓ |
选择排序 | n 2 n^2 n2 | n 2 n^2 n2 | n 2 n^2 n2 | 1 | |
插入排序 | n n n | n 2 n^2 n2 | n 2 n^2 n2 | 1 | ✓ |
希尔排序 | n l o g n nlog_n nlogn | n 4 / 3 n^{4/3} n4/3 | n 4 / 3 n^{4/3} n4/3 | 1 | |
二叉树排序 | n l o g n nlog_n nlogn | n l o g n nlog_n nlogn | n 2 n^2 n2 | n | ✓ |
归并排序 | n l o g n nlogn nlogn | n l o g n nlogn nlogn | n l o g n nlogn nlogn | n | ✓ |
快速排序 | n l o g n nlog_n nlogn | n l o g n nlogn nlogn | n 2 n^2 n2 | l o g n logn logn | |
堆排序 | n l o g n nlog_n nlogn | n l o g n nlogn nlogn | n l o g n nlogn nlogn | 1 | |
计数排序 | n+r | n+r | n+r | ✓ | |
桶排序 | n+r | n+r | n+r | ✓ | |
基数排序 | nk/d | nk/d | n + 2 d n+2^d n+2d | ✓ | |
TimSort | n | n l o g n nlog_n nlogn | n l o g n nlog_n nlogn | n | ✓ |