元素两两比较,如果发生逆序则交换,直到所有记录都排好序为止。
常见的交换排序方法:
冒泡排序:O(n2)
快速排序:O(nlog2n)
冒泡排序(Bubble Sort)是一种最简单的交换排序方法,它通过两两比较相邻记录的关键字,如果发生逆序,则进行交换,从而使关键字小的记录如气泡一般逐渐往上 "漂浮"(左移),或者使关键字大的记录如石块一样逐渐向下 "坠落”(右移)。
void BubbleSort(SqList &L)
{ //对顺序表L做冒泡排序
m=L.length-1;flag=1; // flag用来标记某一趟排序是否发生交换
while ((m>O) && (flag==1))
{
flag=O; // flag置为0,如果本趟排序没有发生交换,则不会执行下一趟排序
for(j =1; j<=m; j++)
if(L.r[j].key>L.r[j+1].key)
{
flag=1; // flag置为1,表示本趟排序发生了交换
t=L.r[j];L.r[j]=L.r[j+1];L.r[j+1]=t; // 交换前后两个记录
}
--m;
}
}
最好情况(正序)
最坏情况(逆序)
对冒泡算法的评价:
快速排序 (Quick Sort) 是由冒泡排序改进而得的。 在 冒泡排序过程中, 只对相邻的两个记录进行比较, 因此每次交换两个相邻记录时只能消除一个逆序。 如果能通过两个(不相邻)记录的一次交换,消除多个逆序, 则会大大加快排序的速度。 快速排序方法中的一次交换可能消除多个逆序。
选定一个中间数作为参考,所有元素与之比较,小的调到其左边,大的调到其右边。
每一趟的子表的形成是采用从两头向中间交替式逼近法;由于每趟中对各子表的操作都相似,可采用递归算法。
int Partition(SqList &L, int low, int high)
{ //对顺序表L中的子表r[low .. high]进行一趟排序,返回枢轴位置
L.r[O]=L.r[low]; // 用子表的第一个记录做枢轴记录
pivotkey=L.r[low].key; // 枢轴记录关键字保存在pivotkey中
while(low<high) // 从表的两端交替地向中间扫描
{
while(low<high&&L.r[high].key>=pivotkey) --high;
L.r[low]=L.r[high]; // 将比枢轴记录小的记录移到低端
while(low<high&&L.r[low].key<=pivotkey) ++low;
L.r[high]=L.r[low]; // 将比枢轴记录大的记录移到高端
}
L.r[low]=L.r[O]; // 枢轴记录到位
return low; // 返回枢轴位置
}
void QSort(SqList &L,int low,int high)
{ // 调用前置初值: low=1; high=L.length;
// 对顺序表L中的子序列L.r[low .. high]做快速排序
if(low<high) { // 长度大于1
pivotloc=Partition(L, low, high); // 将L.r[low.. high] 一分为二,pivotloc是枢轴位置
QSort(L, low, pivotloc-1); // 对左子表递归排序
QSort (L, pivotloc+1, high); // 对右子表递归排序
}
}
void QuickSort(SqList &L)
{ //对顺序表L做快速排序
QSort(L,1,L.length);
}
时间复杂度:
平均计算时间是O(nlog2n);
Qsort():O(log2n)
Partition():O(n)
空间复杂度:
快速排序不是原地排序,由于程序中使用了递归,需要递归调用栈的支持,而栈的长度取决于递归调用的深度。(即使不用递归,也需要用用户栈)。
在平均情况下,需要O(logn)的栈空间。
在最坏情况下:栈空间可达O(n)。
稳定性:
快速排序时一种不稳定的排序方法。
试对(90,85,79,74,68,50,46)进行快速排序的划分,你是否发现什么特殊情况?再对(46,50,68,74,79,85,90)进行快速排序划分呢?
由于每次枢轴记录的关键字都是大于其它所有记录的关键字,致使一次划分之后得到的子序列(1)的长度为0,这时已经退化成为没有改进措施的冒泡排序。