说明
本文参考极客时间—王争·数据结构与算法之美。
个人觉得王争讲得很好,图也很形象。
这里很多字都没有打进来,我觉得配合图片和代码,应该可以了解这个思想了。
如果有需要,大家可以去听一听。
大纲
时间复杂度 | 稳定排序? | 原地排序? | |
---|---|---|---|
冒泡排序 | O(n^2) | 是 | 是 |
插入排序 | O(n^2) | 是 | 是 |
选择排序 | O(n^2) | 否 | 是 |
快速排序 | O(nlogn) | 否 | 是 |
归并排序 | O(nlogn) | 是 | 否 |
堆排序 | O(nlogn) | 是 | 否 |
桶排序 | O(n) | 是 | 否 |
计数排序 | O(n+k) k是数据范围 | 是 | 否 |
基数排序 | O(dn) d是维度 | 是 | 否 |
冒泡排序
void bubbleSort(int *a, int n)
{
if(n <= 1) return;
for(int i = 0; i < n; i++){
bool flag = false; //flag是判断是否无序的标志位
for(int j = 0; j < n-i-1; j++){
if( a[j] > a[j+1] ){
swap( a[j] , a[j+1] );
flag = true;
}
}
if(!flag) break;
}
}
图解
第一次冒泡
冒泡过程:
img
插入排序
插入的过程与冒泡的不一样,插入进行的是比较-幅值,冒泡进行的是比较-交换。
因此插入的效率比冒泡要高。
void insertSort(int *a, int n)
{
if( n <= 1) return;
for(int i = 1; i < n; i++){
int value = a[i]; //value是当前待插入的数
int j = i-1;
for(;j >= 0; j--){ //j是遍历value前面的数
if( a[j] > value ){
a[j+1] = a[j];
}
}
a[j+1] = value;
}
}
图解
插入过程
选择排序
选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。
图解
选择排序过程
快速排序
快速排序用的是分治思想,因此可以用递归算法来实现。
int partition(int *a, int p, int r)
{
int pivot = a[r]; //value可以选取得尽量随机
int i = p;
for(int j = p; j < r; j++){
if( a[j] < value ){
swap( a[i] , a[j] );
i++;
}
}
swap( a[i] , a[r] );
return i;
}
void quickSort(int *a, int p, int r)
{
if( p >= r) return;
int q;
q = partition(a,p,r);
quickSort(a,p,q-1);
quickSort(a,q+1,r);
}
图解
partition函数过程
归并排序
void merge(int *a, int p, int r, int q)
{
int *tmp = new int[r-p+1];
int i = p;
int j = q + 1;
int k = 0;
while( i <= q && j <= r){
if( a[i] <= a[j])
tmp[k++] = a[i++];
else
tmp[k++] = a[j++];
}
int start = j;
int end = r;
if(i <= q){
start = i;
end = q;
}
while( start <= end )
tmp[k++] = a[start++];
for(int n = 0; n < r-p+1; n++)
a[p+n] = tmp[n];
delete[] tmp;
}
void mergeSort(int *a, int p, int r)
{
if( p >= r) return;
int q = (p+r)/2;
mergeSort(a,p,q);
mergeSort(a,q+1,r);
}
图解
思路
merge函数过程
(持续更新中......)
参考资料
极客时间-王争·数据结构与算法之美https://time.geekbang.org/column/article/0?cid=126