名称 | 复杂度 | 说明 | 备注 |
冒泡排序 Bubble Sort | O(N*N) | 将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮 | |
插入排序 Insertion sort | O(N*N) | 逐一取出元素,在已经排序的元素序列中从后向前扫描,放到适当的位置 | 起初,已经排序的元素序列为空 |
选择排序 | O(N*N) | 首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此递归。 | |
快速排序 Quick Sort | O(n *log2(n)) | 先选择中间值,然后把比它小的放在左边,大的放在右边(具体的实现是从两边找,找到一对后交换)。然后对两边分别使用这个过程(递归)。 | |
堆排序Heap Sort | O(n *log2(n)) | 利用堆(heaps)这种数据结构来构造的一种排序算法。堆是一个近似完全二叉树结构,并同时满足堆属性:即子节点的键值或索引总是小于(或者大于)它的父节点。 | 近似完全二叉树 |
希尔排序 SHELL | O(n1+£) 0<£<1 | 选择一个步长(Step) ,然后按间隔为步长的单元进行排序.递归,步长逐渐变小,直至为1. | |
箱排序 Bin Sort | O(n) | 设置若干个箱子,把关键字等于 k 的记录全都装入到第 k 个箱子里 ( 分配 ) ,然后按序号依次将各非空的箱子首尾连接起来 ( 收集 ) 。 | 分配排序的一种:通过 " 分配 " 和 " 收集 " 过程来实现排序。 |
桶排序 Bucket Sort | O(n) | 桶排序的思想是把 [0 , 1) 划分为 n 个大小相同的子区间,每一子区间是一个桶。 |
1.插入排序
稳定
空间复杂度O(1)
时间复杂度O(n^2)
最差情况:反序,需要移动n*(n-1)/2个元素
最好情况:正序,不需要移动元素
void insertsort(int a[], int n)
{
int i, j;
int tmp;
for (i = 1; i < n; i++)
{
tmp = a[i];
for (j = i - 1; j >= 0 && a[j] > tmp; j--)
a[j + 1] = a[j];
a[j + 1] = tmp;
}
}
2.冒泡排序
稳定
空间复杂度O(1)
时间复杂度O(n^2)
最差情况:反序,需要交换n*(n-1)/2个元素
最好情况:正序,不需要交换元素
void bubblesort(int a[], int n)
{
int i, j;
int tmp;
for (i = n - 1; i > 0; i--)
for (j = 0; j < i; j++)
{
if (a[j] > a[j + 1])
{
tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
}
}
3.选择排序
不稳定
空间复杂度O(1)
时间复杂度O(n^2)
最差情况:第一个元素为最大元素,其余元素正序,需要交换n-1个元素(例如:4 3 2 1)
最好情况:正序,不需要交换元素
void selectsort(int a[], int n)
{
int i, j, k;
int tmp;
for (i = 0; i < n - 1; i++)
{
k = i;
for (j = i + 1; j < n; j++)
{
if (a[j] < a[k])
k = j;
}
if (k != i)
{
tmp = a[i];
a[i] = a[k];
a[k] = tmp;
}
}
}
4.希尔排序(缩小增量排序)
不稳定
空间复杂度O(1)
时间复杂度优于插入排序,最坏情况下O(n^2)
最差情况:有n=2^k个元素,偶数位置上有n/2个同为最大的元素,奇数位置上有n/2个同为最小的元素,此时最优最后一趟进行插入排序(例如:1 5 2 6 3 7 4 8)
最好情况:正序,不需要移动元素
void shellsort(int a[], int n)
{
int i, j, increment;
int tmp;
for (increment = n / 2; increment > 0; increment /= 2)
{
for (i = increment; i < n; i++)
{
tmp = a[i];
for (j = i - increment; j >= 0 && tmp < a[j]; j -= increment)
{
a[j + increment] = a[j];
}
a[j + increment] = tmp;
}
}
}
5.堆排序
不稳定
空间复杂度O(1)
时间复杂度O(nlogn)
#define leftchild(i) (2 * (i) + 1)
void percdown(int a[], int i, int n)
{
int child;
int tmp;
for (tmp = a[i]; leftchild(i) < n; i = child)
{
child = leftchild(i);
if (child != n - 1 && a[child + 1] > a[child])
child++;
if (tmp < a[child])
a[i] = a[child];
else
break;
}
a[i] = tmp;
}
void heapsort(int a[], int n)
{
int i;
int tmp;
for (i = n / 2; i >= 0; i--)
{
percdown(a, i, n);
}
for (i = n - 1; i > 0; i--)
{
tmp = a[0];
a[0] = a[i];
a[i] = tmp;
percdown(a, 0, i);
}
}
6.归并排序
稳定
空间复杂度O(n)
时间复杂度O(nlogn)
void merge(int a[], int tmparray[], int lpos, int rpos, int rightend)
{
int i, leftend, numelements, tmppos;
leftend = rpos - 1;
tmppos = lpos;
numelements = rightend - lpos + 1;
while (lpos <= leftend && rpos <= rightend)
{
if (a[lpos] <= a[rpos])
tmparray[tmppos++] = a[lpos++];
else
tmparray[tmppos++] = a[rpos++];
}
while (lpos <= leftend)
tmparray[tmppos++] = a[lpos++];
while (rpos <= rightend)
tmparray[tmppos++] = a[rpos++];
for (i = 0; i < numelements; i++, rightend--)
a[rightend] = tmparray[rightend];
}
void msort(int a[], int tmparray[], int left, int right)
{
int center;
if (left < right)
{
center = (left + right) / 2;
msort(a, tmparray, left, center);
msort(a, tmparray, center + 1, right);
merge(a, tmparray, left, center + 1, right);
}
}
void mergesort(int a[], int n)
{
int *tmparray;
tmparray = (int*)malloc(n * sizeof(int));
if (tmparray != NULL)
{
msort(a, tmparray, 0, n - 1);
free(tmparray);
}
else
printf("No space for tmp array!!!/n");
}
7.快速排序(当小于4个元素时,用到插入排序)
不稳定
空间复杂度O(1)
时间复杂度O(nlogn)
最差情况:要排序的数组基本有序,枢纽每次取最大(小)元素,退化为冒泡算法
最好情况:枢纽两边元素个数基本相同
#define cutoff (3)
void swap(int *a, int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int median3(int a[], int left, int right)
{
int center = (left + right) / 2;
if (a[left] > a[center])
swap(&a[left], &a[center]);
if (a[left] > a[right])
swap(&a[left], &a[right]);
if (a[center] > a[right])
swap(&a[center], &a[right]);
swap(&a[center], &a[right - 1]);
return a[right - 1];
}
void qsort(int a[], int left, int right)
{
int i, j;
int pivot;
if (left + cutoff <= right)
{
pivot = median3(a, left, right);
i = left;
j = right - 1;
for ( ; ; )
{
while (a[++i] < pivot) {}
while (a[--j] > pivot) {}
if (i < j)
swap(&a[i], &a[j]);
else
break;
}
swap(&a[i], &a[right - 1]);
qsort(a, left, i - 1);
qsort(a, i + 1, right);
}
else
insertsort(a + left, right - left + 1);
}
void quicksort(int a[], int n)
{
qsort(a, 0, n - 1);
}
各种排序方法比较
简单排序中直接插入最好(稳定、易于实现),快速排序最快,当文件为正序时,直接插入和冒泡均最佳。
(1)若n较小(如n≤50),可采用直接插入或直接选择排序。
当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插人,应选直接选择排序为宜。
(2)若文件初始状态基本有序(指正序),则应选用直接插人、冒泡或随机的快速排序为宜;
(3)若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。若要求排序稳定,则可选用归并排序。但本章介绍的从单个记录起进行两两归并的排序算法并不值得提倡,通常可以将它和直接插入排序结合在一起使用。先利用直接插入排序求得较长的有序子文件,然后再两两归并之。因为直接插入排序是稳定的,所以改进后的归并排序仍是稳定的。