1. 交换排序
交换排序的基本思想就是:两两比较待排序记录的关键字,如果两个记录的次序相反时即进行交换,直到所有记录中没有反序(反序指的是,大的记录在前,小的记录在后)的记录为止。
图1-交换排序
例如,在图1这个待排序列的所有记录中,如果7和2这两个记录是反序的,那么就交换两个记录的位置,直到这个序列中没有反序的记录,而这个排序的过程就是交换排序。
典型的交换排序算法有:冒泡排序和快速排序,在这两种排序算法中,其核心思想都是通过交换两个反序的记录的位置来完成排序的,但不同的排序算法在进行交换时,采取的策略是不一样的。
2. 冒泡排序
冒泡排序的思想:不停地比较相邻的两个记录,如果相邻的两个记录的次序是反序则交换,直到所有的记录都已经排好序了(使关键字最小的记录如气泡一般逐渐往上“漂浮”直至“水面”,所以叫冒泡排序)。
3. 冒泡排序的过程
假设现在有这样一个待排关键字序列:{23,38,22,45,23,17,31,15,41},这个序列中有n个元素,需要进行n-1趟排序,那么从前往后依次比较相邻的两个关键字,如果是反序的就交换记录,如果不是则继续往后比较。
下面我们来看一下冒泡排序过程的动画演示:
图2-冒泡排序
通过冒泡排序过程可知,每一趟排序完毕后,最大的数都会出现在最后,因此在下一趟比较的时候就可以跳过这个最大的数字了。比如第一趟比较完毕后,最大的数45就会出现在最后的位置,那么在下一趟排序的过程中就不用比较45了,第二趟,第三趟 ….. 以此类推
4. 冒泡排序算法
冒泡排序算法的代码实现:
void BubbleSort(RecType R[] , int n)
{
int i;
int j;
RecType temp;
//比较n-1趟
for(i = 0; i < n-1; i++)
{
for(j = 0; j < n-1-i; j++)
{
//比较相邻的记录,如果是逆序则交换
if(R[j+1].key < R[j].key)
{
temp.key = R[j+1].key;
R[j+1].key = R[j].key;
R[j].key = temp.key;
}
}
}
}
int main(void)
{
RecType R[MAXSIZE] = {0};
int arr[] = {23,38,22,45,23,17,31,15,41};
int i;
printf("--------排序前--------\n");
for(i = 0; i < MAXSIZE; i++)
{
R[i].key = arr[i];
printf("R[%d].key = %d\n" , i , R[i].key);
}
//冒泡排序
BubbleSort(R,MAXSIZE);
printf("--------排序后--------\n");
for(i = 0; i < MAXSIZE; i++)
{
printf("R[%d].key = %d\n" , i , R[i].key);
}
return 0;
}
测试结果:
5. 冒泡排序改进版
其实上面这个冒泡排序算法是有缺点的,比如现在有这样一个接近已完成排序
的关键字序列:{17,15,22,23,23,31,38,41,45},其实只要一趟排序,把17和15交换位置就可以完成排序。但是按照冒泡排序算法还是会进行n-1趟排序,显然,这多出来的几趟排序是有些多余的。
通过仔细分析可以发现,整个排序的过程只需要2趟就可以完成,第一趟交换17和15关键字的位置,然后第二趟在进行比较时,如果没有交换的记录,则说明这个序列已经是有序的了,不需要再继续比较了。
因此我们改进的地方就是,定义一个变量flag检查每一趟排序是否交换了位置, 如果没有交换,此时flag为false,说明序列已经有序了,不需要再继续循环了。
图3-冒泡排序改进版
冒泡排序改进版代码如下:
//冒泡排序改进版
void BubbleSort2(RecType R[] , int n)
{
int i;
int j;
RecType temp;
for(i = 0; i < n-1; i++)
{
//定义标志,默认false
Bool falg = FALSE;
for(j = 0; j < n-1-i; j++)
{
if(R[j+1].key < R[j].key)
{
temp.key = R[j+1].key;
R[j+1].key = R[j].key;
R[j].key = temp.key;
//更改标志
falg = TRUE;
}
}
//判断每一趟排序是否交换位置,如果没有则直接返回
if(falg == FALSE)
{
return;
}
}
}
6. 冒泡排序的性能
1. 对于最好的情况就是待排关键字序列本身就是有序的,如关键字序列:{15,17,22,23,23,31,38,41,45},只需进行一趟冒泡,且移动的次数为0,比较的次数为n-1,也就是说最好的情况下,时间复杂度为
。
2. 最坏的情况就是待排关键字序列本身就是逆序的,如{45,41,38,31,23,23,22,17,15},需要比较n-1趟,且每一趟的比较的次数随着i而变化,比较次数为
,总的比较次数为:
同时,每次比较进行交换位置的时候,都需要三次移动,因此总的移动次数为:
那么最坏情况下,时间复杂度为 ,也就是说冒泡排序算法的平均时间复杂度为 。
3. 对于稳定性来说,冒泡排序算法在比较两个相同的关键字时不会交换位置,因此冒泡排序是一个稳定排序算法。