排序算法四:交换排序之冒泡排序
声明:引用请注明出处http://blog.csdn.net/lg1259156776/
引言
在我的博文《“主宰世界”的10种算法短评》中给出的首个算法就是高效的排序算法。本文将对排序算法做一个全面的梳理,从最简单的“冒泡”到高效的堆排序等。
系列博文的前三篇讲述了插入排序的三种不同类型,本文讲述第二大类的排序算法:交换排序,包括冒泡排序和快速排序。
排序相关的的基本概念
- 排序:将一组杂乱无章的数据按一定的规律顺次排列起来。
- 数据表( data list): 它是待排序数据对象的有限集合。
- 排序码(key):通常数据对象有多个属性域,即多个数据成员组成,其中有一个属性域可用来区分对象,作为排序依据。该域即为排序码。每个数据表用哪个属性域作为排序码,要视具体的应用需要而定。
- 分类
- 内排序:指在排序期间数据对象全部存放在内存的排序;
- 外排序:指在排序期间全部对象个数太多,不能同时存放在内存,必须根据排序过程的要求,不断在内、外存之间移动的排序。
排序算法的分析
排序算法的稳定性
如果在对象序列中有两个对象 r[i] 和 r[j] ,它们的排序码 k[i]==k[j] 。如果排序前后,对象 r[i] 和 r[j] 的相对位置不变,则称排序算法是稳定的;否则排序算法是不稳定的。
排序算法的评价
时间开销
- 排序的时间开销可用算法执行中的数据比较次数与数据移动次数来衡量。
- 算法运行时间代价的大略估算一般都按平均情况进行估算。对于那些受对象排序码序列初始排列及对象个数影响较大的,需要按最好情况和最坏情况进行估算
空间开销
算法执行时所需的附加存储。
交换排序
交换排序的基本思想是:两两比较待排序记录(数据表)的关键字(排序码),发现两个记录的次序相反时即进行交换,直到没有反序的记录为止。主要包括冒泡排序和快速排序。
冒泡排序
算法思想及步骤
冒泡排序是第一个接触也最容易理解的排序算法,因为就像泡泡一样,最轻的率先“冒”出来占据第一的位置,随后是剩下的最轻的再冒出来,占据第二的位置,就这样一步步冒出来,也就完成了排序。
- 对象个数n。最多作最多作n-1趟, i= 0, 2, …, n-1 。
- 第i趟中从后向前j= n-1, n-2, ……, i,顺次两两比较。
- 比较如果发生逆序,则交换V[j-1] 和V[j]。
总之就是每一趟都是将剩余中最大或最小的数据项排在前面已经“冒”出来的数据表后面,遍历完毕也就实现了排序。
算法分析
最好情况:正序排列,比较次数(KCN): n−1 ; 移动次数(RMN):为0。则对应的时间复杂度为 O(n) 。
完全正序排列的话,只需一趟就能判定是否有序,如果遍历j之后发现没有发生逆序就说明已经有序,所以,共比较了 n−1 次,移动0次。
最坏情况:逆序排序,比较次数(KCN): ∑ n−1 i=1 (n−i)=n(n−1)2 ;移动次数(RMN): 3∑ n−1 i=1 (n−i)=3n(n−1)2 。
完全逆序排序的话,第 i 都要比较 n−i 次,而每次比较都要移动3次数据项来交换记录位置。因此总的时间复杂度为 O(n 2 ) 。
它需要一个附加空间,是一个稳定的排序算法。
冒泡排序的c plus plus实现
#include <iostream>
using namespace std;
void BubbleSort(int a[], int size)
{
int i,j,k,temp;
for(i = 0; i < size - 1; i++) {
for(j=0; j < size - 1; j++) {
if(a[j] > a[j+1]) {
temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
}
}
for(k = 0; k < size; k++)
cout << a[k] <<" ";
cout << endl;
}
int dummy = 1;
}
int main()
{
int k;
int a[] = {5,7,1,3,4,9,2,6,8,0};
const size_t sz = sizeof(a)/sizeof(a[0]);
for(k = 0; k < sz; k++)
cout << a[k] <<" ";
cout << endl;
cout << "======================" << endl;
BubbleSort(a,sz);
cout << "======================" << endl;
for(k = 0; k < sz; k++)
cout << a[k] <<" ";
cout << endl;
}
这里给出的BubbleSort其实可以简化,每趟排序都将最大的冒到了最后,所以最后已经排序的不必再进行比较了,从而可以节省一些比较的次数。改进如下:
void BubbleSort(int a[], int size)
{
int i,j,k,temp;
for(i = 0; i < size - 1; i++) {
for(j=0; j < size - 1 - i; j++) {改进之处
if(a[j] > a[j+1]) {
temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
}
}
for(k = 0; k < size; k++)
cout << a[k] <<" ";
cout << endl;
}
int dummy = 1;
}
输出结果如下:
5 7 1 3 4 9 2 6 8 0
//======================
5 1 3 4 7 2 6 8 0 9
1 3 4 5 2 6 7 0 8 9
1 3 4 2 5 6 0 7 8 9
1 3 2 4 5 0 6 7 8 9
1 2 3 4 0 5 6 7 8 9
1 2 3 0 4 5 6 7 8 9
1 2 0 3 4 5 6 7 8 9
1 0 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
//======================
0 1 2 3 4 5 6 7 8 9
注意到,如果此实现中如果给定的数据已经有序,则并不能实现最好情况中所分析的比较次数,因为并没有设定检测每趟是否发生过数据项的交换,如果没有发生数据交换则说明数据表已经有序,则不必进行下一趟的排序了。因此,可以修改代码为:
void BubbleSort(int a[], int size)
{
int i,j,k,temp;
int IsReplaced = 0;
for(i = 0; i < size - 1; i++) {
IsReplaced = 0;
for(j=0; j < size - 1 - i; j++) {
if(a[j] > a[j+1]) {
temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
IsReplaced = 1;
}
}
for(k = 0; k < size; k++)
cout << a[k] <<" ";
cout << endl;
if(IsReplaced==0) break;
}
int dummy = 1;
}
通过测试数据int a[] = {0,1,5,4,3,2,6,7,8,9};进行对比:
未添加数据项交换检查标志的数据输出:
0 1 5 4 3 2 6 7 8 9
======================
0 1 4 3 2 5 6 7 8 9
0 1 3 2 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
======================
0 1 2 3 4 5 6 7 8 9
请按任意键继续. . .
添加数据相交换检查标志的数据输出:
0 1 5 4 3 2 6 7 8 9
======================
0 1 4 3 2 5 6 7 8 9
0 1 3 2 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
======================
0 1 2 3 4 5 6 7 8 9
请按任意键继续. . .
从对比中可以看出,通过判断之后能够提前终止一轮一轮的交换排序,减少不必要的比较过程。