有序序列中,任意一对相邻元素都是顺序排列。
无序序列中,至少有一对相邻元素为逆序排列。
冒泡法是一种经典且稳定的排序方法。它采取相邻元素之间进行比较的策略,若顺序相反则交换元素位置,逐渐将大数沉底,小数上浮(或相反)。在程序上有两种实现方式:
第一种方式
假设数组中有 n 个元素,从第一个元素开始依次与相邻元素做比较,若顺序相反则交换位置。直到最后一个元素参与比较,将会发生 n-1 次元素比较,此刻最大元素必将落位最右端,这个过程我们称为第一轮交换。接着我们重新从第一个元素开始依次与相邻元素做比较,若顺序相反则交换位置,直到倒数第二个元素参与比较(经过第一轮交换最后一个元素必定是最大元素,故无需再参与比较),将会发生 n-2 次比较,此时次大元素必将落位数组的倒数第二个位置……以此类推,每轮比较过后将会减少一个无序元素,第 i 轮参与比较的无序元素个数为 n-i+1,比较次数为 n-i 。容易得知,当我们进行 n-1 轮元素比较之后,将会有 n-1 个元素落位,还剩下 1 个元素必定为最小元素,即所有元素完成排序。
综上所述,完成 n 个元素数组的排序共需要进行 n-1 轮扫描比较,第 i 轮需要进行 n-i 次元素比较。代码如下:
//考虑到数组元素下标从 0 开始,将轮次和比较次数都统一为从 0 开始
void bubblesort(int A[],int n) //或者void bubblesort(int *a,int n),其中n表示数组元素个数。因为数组名本质上也是指针,所以传入数组名可以成功完成元素交换;如果是交换两个变量的值则不可以传入变量名必须传入变量指针
{
for (int i = 0; i < n - 1; i++) // i的取值范围为[0,n-2],共n-1轮
{
for (int j = 0; j < n - 1 - i; j++) // j的取值范围为[0,n-2-i],共n-1-i次。前面我们分析时说过,第i轮,需做n-i次比较,为什么这里又变成了n-1-i次呢?这是因为我们分析时考虑的是轮次i从1开始计算,第1轮无序元素为n个,因此要比较n-1次。而这里我们设定轮次i从0开始计算,即i=0时其实是第1轮,i=1时其实是第2轮……因此我们计算比较次数时将轮次计算为i+1,即第i轮需要比较n-(i+1)=n-1-i次,0 ~ n-1-(i+1)为n-1-i次
{
if (A[j]>A[j + 1])
{
A[j] = A[j] + A[j + 1];
A[j + 1] = A[j] - A[j + 1];
A[j] = A[j] - A[j + 1];
}
}
}
}
//或者更直观些:令轮次从 1 开始,比较次数从 1 开始
void bubblesort(int A[],int n)
{
for (int i = 1; i < n; i++) //i的取值范围为[1,n-1],共n-1轮
{
for (int j = 1; j <= n - i; j++) //第i轮,需做n-i次比较,取值范围为[1,n-i]
{
if (A[j-1]>A[j])
{
A[j-1] = A[j-1] + A[j]; //注意j从1开始取值,而数组元素下标从0开始,因此操作数组元素时应从j-1开始
A[j] = A[j-1] - A[j];
A[j-1] = A[j-1] - A[j];
}
}
}
}
第二种方式
第二种实现方式是第一种实现方式的改进版。算法思想仍然是从数组头部开始逐轮扫描数组元素,依次比较每一对相邻元素,逆序则进行交换。区别是,这次我们设置一个有序标志,标识数组是否完成排序。在一轮扫描开始前,将有序标志初始化为true,然后进行该轮次扫描。若扫描过程中发现了逆序元素,则交换逆序元素并将有序标志更改为false,否则不做任何操作。扫描过程结束后判断有序标志是否为真,为真直接跳出循环,为假则继续新一轮的扫描。代码如下:
void bubblesort(int A[],int n)
{
for(bool sorted=false;sorted=!sorted/*每一轮扫描前将有序标志设置为true*/;n--)
for(int i=1;i<n;i++)
if (A[j-1]>A[j])
{
A[j-1] = A[j-1] + A[j];
A[j] = A[j-1] - A[j];
A[j-1] = A[j-1] - A[j];
sorted=false;
}
}
//或者写的更直观一些,很容易看出是第一种实现方式的改进版,若原始序列的已排序程度较高可显著减少比较次数和轮次,交换次数不变
void bubblesort(int A[],int n)
{
for(int i = 1; i < n; i++)
{
bool sorted=true;
for(int j = 1; j <= n - i; j++)
if (A[j-1]>A[j])
{
A[j-1] = A[j-1] + A[j];
A[j] = A[j-1] - A[j];
A[j-1] = A[j-1] - A[j];
sorted=false;
}
if(sorted==true)
break;
}
}