说明:以下程序的运行和编译都是基于Siemens Totally Integrated Automation Portal V16.0。
冒泡排序原理:
该算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同沸腾的气泡最终会上浮到顶端一样,故名“冒泡排序”。若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C和记录移动次数M均达到最小值。Cmin = N-1, Mmin = 0.
如果数据是反序的,则需要进行n-1趟排序。每趟排序要进行n-i次比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值。
次数比较:Cmax = N(N-1)/2 = O(N2)
移动(交换)次数:Mmax = 3N(N-1)/2 = O(N2)
举例:我们比较完全反序的一组序列:a[]={5,4,3,2,1}
第①趟需要比较4次。
1) a[0]和a[1]比较,5>4,交换顺序。序列变为:{4,5,3,2,1}
2) a[1]和a[2]比较,5>3,交换顺序。序列变为:{4,3,5,2,1}
3) a[2]和a[3]比较,5>2,交换顺序。序列变为:{4,3,2,5,1}
4) a[3]和a[4]比较,5>1,交换顺序。序列变为:{4,3,2,1,5}
经第①趟比较完后,我们发现,该序列中最大的元素依次被移到右边,也就是依次 “下沉”,等到比较第二趟的时候,最后一个元素不再需要进行比较,因此第二趟比较次数会比上一趟少一次,变为3次。
第②趟需要比较3次。
1) a[0]和a[1]比较,4>3,交换顺序。序列变为:{3,4,2,1,5}
2) a[1]和a[2]比较,4>2,交换顺序。序列变为:{3,2,4,1,5}
3) a[2]和a[3]比较,4>1,交换顺序。序列变为:{3,2,1,4,5}
经第②趟比较完后,我们发现,该序列中次最大的元素依次被移到右边,也就是依次 “下沉”,等到比较第三趟的时候,最后二个元素不再需要进行比较,因此第三趟比较次数会比上一趟少一次,变为2次。
第③趟需要比较2次。
1) a[0]和a[1]比较,3>2,交换顺序。序列变为:{2,3,1,4,5}
2) a[1]和a[2]比较,3>1,交换顺序。序列变为:{2,1,3,4,5}
第④趟需要比较1次。
1) a[0]和a[1]比较,2>1,交换顺序。序列变为:{1,2,3,4,5}
最终该无序序列变为我们预期的升序序列,因为该序列完全反序,因此比较次数和移动次数均达到了最大。
比较次数:4+3+2+1=10.
移动(交换次数):3 × (4+3+2+1)=30.
程序示例:外层循环变量控制趟数,内层循环变量控制次数。

冒泡排序之变量定义
(****************************Bubble Sort ****************************)
data:2020-11-10
program function:Bubble Sort
//*****************************************************************//
//caculate array lower limit and upper limit,and array length size
#stat_First := LOWER_BOUND(ARR := #array, DIM := #Array_dimension);
#stat_Last := UPPER_BOUND(ARR := #array, DIM := #Array_dimension);
#stat_ArrayLength := #stat_Last - #stat_First + 1;
IF #stat_ArrayLength < 2 THEN
#error := true;
#status := #ArrayElement_Low;
END_IF;
REGION Ascending
IF (NOT #SortDirection) AND (#stat_ArrayLength >= 2) THEN FOR #i := 0 TO #stat_ArrayLength - 2 DO
FOR #j:=#stat_First TO #stat_Last-1-#i DO ;
IF (#array[#j] > #array[#j + 1]) THEN
#stat_exchange := #array[#j];
#array[#j] := #array[#j + 1];
#array[#j + 1] := #stat_exchange;
END_IF;
END_FOR;
END_FOR;
END_IF;
END_REGION
REGION Descending //降序,从大到小
IF #SortDirection AND ((#stat_ArrayLength >= 2))THEN
FOR #i := 0 TO (#stat_ArrayLength-2) DO
FOR #j := #stat_First TO (#stat_Last -1 - #i) DO
IF (#array[#j] < #array[#j + 1]) THEN
#stat_exchange := #array[#j];
#array[#j] := #array[#j + 1];
#array[#j + 1] := #stat_exchange;
END_IF;
END_FOR;
END_FOR;
END_IF;
END_REGION
运行结果示例:

主程序中调用

排序结果(升序)
优化(一)
对于一组已经有顺序排列的数组或者序列,经第一趟比较完之后,如果没有发生任何的数据交换,那么说明这组序列已经是有序的,不需要在进行第二趟、第三趟...,第n趟的扫描比较。假设我们现在排序a[]={1,2,3,4,5,6,7,8,10,9}这组数据,按照上面的排序方式,第一趟排序后将10和9交换已经有序,接下来的8趟排序就是多余的,什么也没做。所以我们可以在交换的地方插个旗子(立flag),如果那一趟排序没有交换元素,说明这组数据已经有序,不用再继续扫描比较下去。先输出一遍未优化的程序,具体排序过程如下(为了更加直观比较,在VC++环境下测试):

比较次数和趟数(轮数)
接着再输出一遍经过优化(一)的结果:

优化(一)输出结果
#include<iostream>
using namespace std;
int main()
{
int i,j,k,temp,flag,count=0;
int num[]={9,0,1,2,3,4,5,6,7,8};
int len = sizeof(num)/sizeof(int);
int cishu=1;
int Total_cishu=0;
cout<<"-----输出原始序列-----"<<endl;
cout<<" ";
for(int m=0;m<len;m++)
cout<<num[m]<<",";
cout<<endl;
cout<<"-----输出排序过程-----"<<endl;
for(i=0;i<len;i++)
{
flag=0; //每轮都默认没有发生交换
for(j=0;j<len-i-1;j++)
{
if(num[j]>num[j+1])
{
temp=num[j];
num[j]=num[j+1];
num[j+1]=temp;
flag=1; //发生交换
}
Total_cishu++;
if(cishu<10) //10之前的数为了对齐10以上的数,特意在前面输出一个0
cout<<0<<cishu++<<": ";
else
cout<<cishu++<<": "; //10以上的数,直接输出即可
for(k=0;k<len;k++)
if(k<len-1) //每组序列最后一个元素不带逗号
{
cout<<num[k]<<",";
}
else
{
cout<<num[k];
cout<<endl;
}
}
count++; //记录交换轮次
if(flag==0)//当没有发生交换时就退出循环
break;
}
cout<<"比较轮数:"<<count<<endl;
cout<<"比较次数:"<<Total_cishu<<endl;
return 0;
}
可见,经过第一种优化过的程序在进行比较的时候,次数明显减少。只进行了2轮排序,总共9+8=17次。但是我们会发现一个问题,前9次比较此序列已经有序,从第10次开始每次都是重复性的工作。因此我们希望能够一轮比较(9次)就可以得出结果。为此我们继续第二种优化(缩小轮数优化)。
优化(二)
优化(一)仅仅适用于连片有序而整体无序的数据(例如:1,2,3 ,4 ,7,6,5)。但是对于前面大部分是无序而后边小半部分有序(片段有序)的数据{1,2,5,7,4,3,6,8,9,10}排序效率也不可观,对于这种类型序列,我们可以继续优化。即我们可以记下最后一次交换的位置,后边没有交换,必然是有序的,然后下一次排序从第一个比较到上次记录的位置结束即可。

优化(三)轮次和比较次数
#include<iostream>
using namespace std;
int main()
{
int num[]={1,2,5,7,4,3,6,8,9,10};
int len = sizeof(num)/sizeof(int);
int j,k,temp,count=0;
int Total_cishu=0;
int flag=len; //默认比较轮次为N 轮
while(flag>0)
{
k=flag; //用k记录每一轮的比较次数
flag=0;
for(j=1;j<k;j++)
{
if(num[j-1]>num[j])
{
temp=num[j];
num[j]=num[j-1];
num[j-1]=temp;
flag=j; //记录符合交换条件的最终位置
Total_cishu++;
for(int i=0;i<len;i++)
if(i<len-1) //每组序列最后一个元素不带逗
{
cout<<num[i]<<",";
}
else
{
cout<<num[i];
cout<<endl;
}
}
}
if(flag)
count++; //记录比较轮次
}
cout<<"比较轮数count="<<count<<endl;
cout<<"比较次数Total_cishu="<<Total_cishu<<endl;
return 0;
}