算法工程师必备知识-排序(总结对比相关复杂度及C++实现代码)
- 排序的稳定是指保证排序前两个相等的数在序列中的顺序和排序后相等。
可以这样来记:堆排序算法和归并算法是最好最坏平均时间复杂度都是o(NlogN),但是归并算法是稳定性算法,所以他比堆排序需要更多的空间复杂度为o(N),而堆排序只需要O(l)的时间复杂度。另外除了快速排序,希尔排序,堆排序,直接选择排序以外其他的排序算法都是稳定性算法。快速排序在元素基本无序的情况下是最好的选择,在基本递增或者递减中时间复杂度是O(n^2),归并排序和堆排序稳定在o(nlogn),插入排序在基本有序的情况下是最好的,时间复杂度是O(n),另外在数据规模较小时插入排序效果也是很不错的,一般不选择传统的冒泡排序。 - 外部排序常用的算法是归并排序。外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换以达到排序的目的。
- 快速排序算法特点是越有序越慢。
- 基数排序只适用于整数。基数排序适合并行处理排序算法。
- 插入排序在最好情况下的时间复杂度是O(N),快速排序最好时间复杂度是O(NlogN)
- 关于关键字比较的次数与记录的初始排列次序有关的是:冒泡排序,插入排序,希尔排序,快速排序,剩下的都无关。
1.快速排序
快速排序的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
该方法的基本思想是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
具体过程可以看做是挖坑填树的过程:
1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。
2.j–由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。
- 快速排序的时间复杂度在最坏情况下是O(N2),平均的时间复杂度是O(N * lgN),最好时间复杂度是O(NlogN)。可以这样理解:假设被排序的数列中有N个数。遍历一次的时间复杂度是O(N),需要遍历多少次呢?至少lg(N+1)次,最多N次。
(01) 为什么最少是lg(N+1)次?快速排序是采用的分治法进行遍历的,我们将它看作一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树的定义,它的深度至少是lg(N+1)。
因此,快速排序的遍历次数最少是lg(N+1)次。
详细代码如下:
#include<iostream>
using namespace std;
void quickSort(int array[],int L,int R);
int main()
{
int a[]={13,65,22,43,27,5,70,10,3,70},k;
int len=sizeof(a)/sizeof(int);
cout<<"The orginal array are:"<<endl;
for(k=0;k<len;k++)
cout<<a[k]<<",";
cout<<endl;
quickSort(a,0,len-1);
cout<<"The sorted array are:"<<endl;
for(k=0;k<len;k++)
cout<<a[k]<<",";
cout<<endl;
system("pause");
return 0;
}
void quick_sort (int array[],int L, int R)
{ if (L<R)
{ int i=L,j=R,base=array[L];
while (i<j)
{ while (i<j && array[j]>=base)//从右向左找第一个小于base的数,这里只要i<j和array[j]大于等于就要继续移动否则判断
j--; //否是由于找到了小于的数退出,如果是则填坑并移动i
if (i<j)
array[i++]=array[j];
while (i<j && array[i]<=base)
i++;
if (i<j)
array[j--]=array[i]
}
array[i]=base;
quick_sort(array[],L,i-1)//递归调用
quick_sort(array[],i+1,R)
}
}