先来看基本的冒泡排序:
原理很简单,两层循环,外层确定遍历趟数,内层遍历数组,按照排序规则正序或者倒序比较相邻两个数字大小,并交换他们的位置。
static void Main(string[] args)
{
int[] nums = new int[] { 8,7,6,5,4,3,2,1,0 };
NumOrder(nums);
}
static void NumOrder(int[] nums)
{
int ci = 0;
int temp = 0;
for (int i = 0; i < nums.Length - 1; i++)
{
for (int j = 0; j < nums.Length - i - 1; j++)
{
if (nums[j] > nums[j + 1])
{
temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
ci++;
}
}
foreach (var num in nums)
{
Console.Write(num + ",");
}
Console.WriteLine($"循环了{ci}次");
}
但是现在好像不管要排序的数组是不是有序,都需要循环这么多遍!当我们把数组换成有序的,循环次数还是跟之前无序的一样。我们可以来优化一下,当第一次循环发现无位置交换的情况下,就证明这个数组是有序的。我们就可以结束循环,返回数组了
static void Main(string[] args)
{
int[] nums = new int[] { 0,1,2,3,4,5,6,7,8};
NumOrder(nums);
}
static void NumOrder(int[] nums)
{
int ci = 0;
int temp = 0;
int isOrderly=1;//默认为有序
for (int i = 0; i < nums.Length - 1; i++)
{
for (int j = 0; j < nums.Length - i - 1; j++)
{
if (nums[j] > nums[j + 1])
{
temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
isOrderly=0;//如果位置发生了交换,就设置为无序
}
ci++;
}
if (isOrderly == 1)//如果第一次内层循环未发生位置交换,就代表数组是有序的,所以跳出循环
break;
}
foreach (var num in nums)
{
Console.Write(num + ",");
}
Console.WriteLine($"循环了{ci}次");
}
循环次数变成了8次。
那我们还能再优化优化么?肯定可以耶,我们来仔细分析一下排序过程:
你会发现从最后一次比较的位置下标开始,前面部分都是无序的,后面都是有序的已比较过的:
所以,后半部分已经有序的,下次循环排序时候是不是可以避免掉。我们可以通过记录最后一次发生位置交换的下标,用这个下标来控制内循环次数。看代码:
static void Main(string[] args)
{
int[] nums = new int[] { 9, 8, 7, 6, 5,10,11,12,13,14,15};
NumOrder(nums);
}
static void NumOrder(int[] nums)
{
int ci = 0;
int temp = 0;
int sub = nums.Length - 1;//控制内层循环次数
int n;//记录最后一次交换的位置
int isOrderly;
for (int i = 0; i < nums.Length-1; i++)
{
n = 0;
isOrderly = 1;//默认为有序
for (int j = 0; j < sub; j++)
{
if (nums[j] > nums[j + 1])
{
temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
isOrderly = 0;//如果位置发生了交换,就设置为无序
n = j;//记录最后一次交换的位置
}
ci++;
}
if (isOrderly == 1)//如果第一次内层循环未发生位置交换,就代表数组是有序的所以跳出循环
break;
sub = n;
}
foreach (var num in nums)
{
Console.Write(num + ",");
}
Console.WriteLine($"循环了{ci}次");
}
可以用数组测试一下,比如代码里的那个,改动之前运行结果:
记录下标,控制内循环次数之后: