C# 冒泡排序
冒泡排序原理(以数组从小到大排列为例,假设数组从左到右排列):
在数组最左侧开始,两个相邻的数两两对比,如果靠左的数值大于靠右的数值,则两个数交换位置,否则不变位置;
之后整体右移一位,再次与相邻的数相比;最终将数组中最大的数值移到最右侧,这个过程为第一趟排序,需要经过n-1次比较;
之后开始第二趟循环,将数组中第二大的数值移到最大数的前一位,依此类推。
图文讲解冒泡排序(从小到大排序)
第一趟 共进行n-1次比较;
例如数组{4,8,7,5,2,3,6,1};共8个数,需要比较7次;
第一次比较:4和8比较,不满足换位条件,保持不变;
第一次比较结果:
第一次比较结束后,右移一位,进行第二次比较;
第二次比较:8和7比较,满足换位条件,8和7互换;
第二次比较结果:
第二次比较结束后,右移一位,进行第三次比较;
第三次比较:8和5比较,满足换位条件,8和5互换;
第三次比较结果:
第三次比较结束后,右移一位,进行第四次比较;
第四次比较:8和2比较,满足换位条件,8和2互换;
第四次比较结果:
第四次比较结束后,右移一位,进行第五次比较;
第五次比较:8和3比较,满足换位条件,8和3互换;
第五次比较结果:
第五次比较结束后,右移一位,进行第六次比较;
第六次比较:8和6比较,满足换位条件,8和6互换;
第六次比较结果:
第六次比较结束后,右移一位,进行第七次比较;
第七次比较:8和1比较,满足换位条件,8和1互换;
第七次比较结果:
至此第一趟排序结束,结果就是将最大的数8 移到数组的最右侧。
依照如上第一趟的排序接着进行第二趟,从4和7的比较开始,找到数组中第二大的数值,依次类推直到排序结束。
C#编程写法
直接上程序吧
Static void Main(){
int[] _arrayNa = { 4, 8, 7, 5, 2, 3, 6, 1 };
Console.WriteLine("排序前:");
foreach (int Num in _arrayNa) //按索引顺序输出数组元素
{
Console.Write(Num + " ");
}
Console.Write("\n");
for (int i = 0; i < _arrayNa.Length-1; i++)
{
for (int j = 0; j < _arrayNa.Length - 1; j++)
{
int k = j + 1;
if (_arrayNa[j] > _arrayNa[k]) //判断是否满足交换条件
{
//交换位置
_arrayNa[j] = _arrayNa[j] + _arrayNa[k];
_arrayNa[k] = _arrayNa[j] - _arrayNa[k];
_arrayNa[j] = _arrayNa[j] - _arrayNa[k];
}
}
Console.WriteLine("第{0}趟排序后:", i + 1);
foreach (int Num in _arrayNa)
{
Console.Write(Num + " ");
}
Console.Write("\n");
}
Console.WriteLine("最终排序:");
foreach (int Num in _arrayNa)
{
Console.Write(Num + " ");
}
Console.Write("\n");
Console.ReadKey();
}
最终程序运行结果:
冒泡排序的优化
冒泡排序的优化一般涉及两个部分:内层循环的优化及外层循环的优化;
一、内层循环优化
1.内层循环缺陷:如之上脚本,可以观察到,每趟排序都需要比较n-1次,然而在排到第二趟时,7和8无须比较,因为在第一趟循环中8已经是比较出来的最大值; 比如第三趟排序时,无需比较最后两个数,因为最右侧两个数已经是正确排序了;如此看下来除去第一趟排序需要n-1次比较,后面几趟都无需比较n-1次;
2.内层循环优化策略:了解缺陷所在之后,针对缺陷做出优化,方法有两种:
——增加标志位,在内层循环中一旦出现没有交换位置的情况,则记住该位置,之后下次排序时比较的长度变为从开始到标志位的位置,标志位后面的不再进行比较;
优化后:
int[] _arrayNa = { 4, 6, -2, 3, 1 ,5};
Console.WriteLine("排序前:");
foreach (int Num in _arrayNa) //按索引顺序输出数组元素
{
Console.Write(Num + " ");
}
Console.Write("\n");
int index = _arrayNa.Length - 1;
int flag = 0;
for (int i = 0; i < _arrayNa.Length-1; i++)
{
for (int j = 0; j < index; j++)
{
int k = j + 1;
if (_arrayNa[j] > _arrayNa[k])
{
_arrayNa[j] = _arrayNa[j] + _arrayNa[k];
_arrayNa[k] = _arrayNa[j] - _arrayNa[k];
_arrayNa[j] = _arrayNa[j] - _arrayNa[k];
flag = j; //标记最后一次交换的位置
}
Console.WriteLine("第{0}趟排序后:第{1}次比较", i + 1,k);
}
index = flag;
Console.WriteLine("index:"+index.ToString());
Console.WriteLine("第{0}趟排序后:", i + 1);
foreach (int Num in _arrayNa)
{
Console.Write(Num + " ");
}
Console.Write("\n");
}
Console.WriteLine("最终排序:");
foreach (int Num in _arrayNa)
{
Console.Write(Num + " ");
}
Console.Write("\n");
Console.ReadKey();
优化后输出结果:
由输出结果可以看出优化后每趟排序都进行忽略后面已经排好的几个元素;
内层循环优化的第二种方式:
因为每趟排序一定能找出最大的数字排到最右侧,所以可以设置每趟排序后内层循环数组长度减一,把上一趟排序的最后一个元素排除掉;
内层循环优化方法2:
int[] _arrayNa = { 4, 6, -2, 3, 1 ,5};
Console.WriteLine("排序前:");
foreach (int Num in _arrayNa) //按索引顺序输出数组元素
{
Console.Write(Num + " ");
}
Console.Write("\n");
for (int i = 0; i < _arrayNa.Length-1; i++)
{
for (int j = 0; j < _arrayNa.Length-1-i; j++)
{
int k = j + 1;
if (_arrayNa[j] > _arrayNa[k])
{
_arrayNa[j] = _arrayNa[j] + _arrayNa[k];
_arrayNa[k] = _arrayNa[j] - _arrayNa[k];
_arrayNa[j] = _arrayNa[j] - _arrayNa[k];
}
Console.WriteLine("第{0}趟排序后:第{1}次比较", i + 1,k);
}
Console.WriteLine("第{0}趟排序后:", i + 1);
foreach (int Num in _arrayNa)
{
Console.Write(Num + " ");
}
Console.Write("\n");
}
Console.WriteLine("最终排序:");
foreach (int Num in _arrayNa)
{
Console.Write(Num + " ");
}
Console.Write("\n");
Console.ReadKey();
二、外层循环优化
外层循环缺陷:从内层循环优化案例输出结果来看,第3趟排序后已经完成了整体数组的排序,第4趟及第5趟完全没有必要;
外层循环优化:与内层循环类似,如果上一趟排序中未发生元素的位置交换,说明上一趟排序已经完成,无须进行下面的排序了;
基于内层循环优化案例1完成外层循环优化,优化后:
int[] _arrayNa = { 4, 6, -2, 3, 1 ,5};
Console.WriteLine("排序前:");
foreach (int Num in _arrayNa) //按索引顺序输出数组元素
{
Console.Write(Num + " ");
}
Console.Write("\n");
int index = _arrayNa.Length - 1;
int flag = 0;
for (int i = 0; i < _arrayNa.Length-1; i++)
{
bool isChange = false; //每趟排序完成后置为false
for (int j = 0; j < index; j++)
{
int k = j + 1;
if (_arrayNa[j] > _arrayNa[k])
{
_arrayNa[j] = _arrayNa[j] + _arrayNa[k];
_arrayNa[k] = _arrayNa[j] - _arrayNa[k];
_arrayNa[j] = _arrayNa[j] - _arrayNa[k];
flag = j; //标记最后一次交换的位置
isChange = true; //判断是否发生了交换
}
Console.WriteLine("第{0}趟排序后:第{1}次比较", i + 1,k);
}
index = flag;
Console.WriteLine("index:"+index.ToString());
Console.WriteLine("第{0}趟排序后:", i + 1);
foreach (int Num in _arrayNa)
{
Console.Write(Num + " ");
}
Console.Write("\n");
//如果上一趟排序未发生交换,则终止外层循环
if (!isChange)
{
// i = _arrayNa.Length;
break;
}
}
Console.WriteLine("最终排序:");
foreach (int Num in _arrayNa)
{
Console.Write(Num + " ");
}
Console.Write("\n");
Console.ReadKey();
优化后输出结果:
由于第三趟发生了元素的位置交换,因此有了第四趟排序;第四趟排序未发生位置交换,因此第五趟排序被省略。