冒泡排序基本是我们接触计算机所学到的第一个排序算法,简单的其中其实蕴藏着一些小小的优化,下面就是我分享的一点小小思路
最初版冒泡排序
public static int BubbleSort(double[] arr)
{
int count = 0; //记录执行次数
double temp;
for(int i = 0; i < arr.Length - 1; i++)
{
for(int j = 0; j < arr.Length-1-i; j++)
{
if (arr[j] > arr[j + 1])
{
//swap
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
count++;//内循环count
}
count++;//外循环count
}
return count;
}
我们给出的数组如下:
编译运行结果如下
可以看到执行了28次,即1累加到7次的内循环再加7次外循环
考虑到排序过程中若后有序则无需继续考虑,于是可优化内循环,即将最后交换位置作为内循环的上限,即代表后有序后不在考虑后方,优化如下:
public static int BubbleSort(double[] arr)
{
int count = 0;//记录执行次数
double temp;
int k = arr.Length - 1;
int lastPos = 0; //最后一次交换位置
for (int i = 0; i < arr.Length - 1; i++)
{
for(int j = 0; j < k; j++)
{
if (arr[j] > arr[j + 1])
{
//swap
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
lastPos = j;
}
count++;
}
count++;
k = lastPos;
}
return count;
}
执行结果如下
可以看到明显少了4次的执行次数(来自内循环),我们将数组换为后有序的能更加明显的看出优化:数组换成
double[] arr = { 1, 2, 10, 9, 6, 8, 9, 10, 12, 14, 0, 3, 15, 16};
原始冒泡排序结果如下:
内循环优化后的执行结果如下:
可以看到,随着后有序的增加,冒泡次数也随之减少,但这时有认真的同学注意到了,为啥我传了个完全有序的数组还执行了7次呢,我们是不是应该定义个bool变量来记录它是否有序,如果第一次未交换则bool值为true,则让它跳出外循环。其实仔细一想,还记得冒泡排序的外循环的意义吗?外循环代表的是已经排好序的个数,再优化外循环如下:
public static int BubbleSort(double[] arr)
{
int count = 0;//记录执行次数
double temp;
int k = arr.Length - 1;
int lastPos;//最后一次交换位置
for (int i = 0; i <k+1; i++)
{
lastPos=0;//关键,可替代bool值
for(int j = 0; j < k; j++)
{
if (arr[j] > arr[j + 1])
{
//swap
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
lastPos = j;
}
count++;//内循环count
}
count++;//外循环count
k = lastPos;
i=0;//关键
}
return count;
}
对同一个数组执行结果如下:
可以看到现在只执行了76次,这时长得帅的同学要问了,为什么是k+1,为什么i要归0,以及为什么lastPos为0可以代替bool值,这三个问题就请大家思考了
既然现在已经优化了后有序了,自然而然我们想到那如何处理前有序呢?,没错,这里给出两种不同的思路。第一种就是双向冒泡或者形象点说上浮和下沉:
public static int BubbleSort(double[] arr)
{
int count = 0;//记录循环次数
double temp;
int k = arr.Length - 1;
int m = 0;
int lastPos;//记录后有序位置
int prePos = arr.Length - 1;//记录前有序位置
for (int i = 0; i < k + 1-m; i++)
{
//上浮
lastPos = 0;
for (int j = m; j < k; j++)
{
if (arr[j] > arr[j + 1])
{
//swap
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
lastPos = j;
}
count++;//记录内循环1
}
k = lastPos;
//下沉
//prePos = arr.Length - 1;//可加可不加
for (int j = k; j > m; j--)
{
if (arr[j] < arr[j - 1])
{
//swap
temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
prePos = j;
}
count++;//记录内循环2
}
m = prePos;
count++;//记录外循环
i=0;//关键,关键,还是tmd关键
}
return count;
}
运行结果如下:
嗯,基本达到预期效果,与原始冒泡相比基本缩短了一半的执行次数
,即使非前后有序,在过程中凑成的前有序或者后有序也能减少其运行次数
第二种方式暂时还没做好,可能要先鸽一段时间,请见谅
这些就是我能分享给大家的全部了,其实还有一些部分可以进行优化,但能力和精力有限,再加上是第一次写csdn,还请各位大佬多多指教!