排序算法学习之交换排序(冒泡排序,快速排序)
冒泡排序
名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
步骤:
进行第i趟排序:
- 比较第1个记录和第2个记录关键字,逆序则交换两个记录
- 往后比较第2个记录和第3个记录,逆序则交换两个记录
- 依次往后比较,直到比较完第n-i个记录和第n-i+1个记录,逆序则交换两个记录
- 关键字最大的记录被交换到第n-i+1的位置
正序:只进行一趟排序,并且只比较n-1次,无需交换记录
逆序:n个元素序列共进行n-1趟排序,需进行,共n(n-1)/2次比较
void BubbleSort(SqList *L)
{
int n=L->length;//表长度
for(int i=1;i<n;i++)//n个元素只进行n-1趟排序
for(int j=0;j<n-i;j++)//从第0个元素开始和后面的元素比较,直到第n-i和第n-i+1个记录比较完
{
if(L->r[j]>L->r[j+1])
{
int key=L->r[j];//交换记录
L->r[j]=L->r[j+1];
L->r[j+1]=key;
}
}
return;
}
冒泡排序时间复杂度为O(n^2)
快速排序
快速排序是对冒泡排序的一种改进。
基本思想:通过一趟排序将待排序记录分割成2个独立的部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
##此处只能选L->r[low]作为枢值,选其他作为枢值会出现枢值丢失(被覆盖)的情况
int Partition(SqList *L,int low,int high)
{
int temp=L->r[low];//临时变量保存枢轴值
while(low<high){//当low==high 说明已经找到枢轴位置,可以区分大小左右序列
while(low<high&&L->r[high]>=temp) --high;//大于等于枢轴值,则继续从后往前扫描,
L->r[low]=L->r[high];//将小于枢轴的值赋值给低位
while(low<high&&L->r[low]<=temp) low++;//小于等于枢轴值,则继续从前往后扫描
L->r[high]=L->r[low];//将大于枢轴的值赋值给高位
}
int final=low;//枢值最后位置
L->r[final]=temp;//用临时变量填回
return final;//返回位置
}
void QSort(SqList *L,int low,int high)
{
if(low<high){//长度必须大于1,等于1就无需再继续排序
int index=Partition(L,low,high);//返回枢轴的位置,左边均小于枢轴,右边均大于等于枢轴
QSort(L,low,index-1);//对枢轴左边序列就进行排序
QSort(L,index+1,high);//对枢轴右边序列进行排序
}
return;
}
void QuickSort(SqList *L)//对顺序表L进行快速排序
{
QSort(L,0,L->length-1);//记录从0开始,length-1结束
return;
}
时间复杂度
- 当序列为正序时,选取第一个记录为枢轴,则左边为空,右边为正序子序列,但是虽然不用赋值,但是比较次数很多,时间复杂度为 O(n^2)
- 当序列为为逆序时,此时快速排序类似于low和high记录前后交换,并且low++,high--,颠倒逆序为正序
- 当序列数据随机分布时,以第一个关键字为基准分为两个子序列,两个子序列的元素个数接近相等,此时执行效率最好。
数据越随机分布时,快速排序性能越好;数据越接近有序,快速排序性能越差。
空间复杂度: 在每次分割的过程中,需要 1 个空间存储枢值。而快速排序大概需要 logN次的分割。
算法稳定性 : 相等元素可能会因为数据的交换导致前后顺序改变,所以快速排序是不稳定的。
通常,快速排序被认为是平均性能最好的排序方法,时间复杂度为:O(nlogn)。