冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,若是顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工做是重复地进行直到没有相邻元素须要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是由于越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端同样,故名“冒泡排序”。算法
public void Sort1(int[] num) { Console.WriteLine("方法1:"); var count1 = 0; var count2 = 0; for (var i = 0; i < num.Length - 1; i++) { for (var j = i + 1; j < num.Length; j++) { count1++; if (num[i] <= num[j]) continue; count2++; var temp = num[j]; num[j] = num[i]; num[i] = temp; } } Console.WriteLine($"结果:{string.Join(",", num)};循环次数:{count1};数据交换次数:{count2}"); }
以上内容,除了代码所有来自网络,盗图请谅解,由于作这么个图是在太难了后端
经过代码,咱们发现,冒泡算法的运行次数其实应该是 (n-1)+(n-2)+(n-3)+...,等于 n(n-1)/2 次,因此得出结论,冒泡算的时间复杂度是O(n^2)数组
可是,上面介绍不是说有最好的状况下(数据自己就是从小到大排列)时间复杂度是 O(n)吗?网络
其实,上面的图片介绍和实际算法都存在一些问题,是有能够优化的地方的。测试
从文字角度上说,既然叫冒泡算法,咱们想一想水里气泡的形态,通常都是从底部升起到水面,因此,为了更符合实际状况,咱们的比较工做应该从数组的尾部开始,把最小(从小到大)的元素慢慢移动到数组的最前的位置。优化
因此这里先修改一版代码,把比较的顺序倒过来,不是把最大的日后移,而是把最小的往前移code
public void Sort2(int[] numbers) { Console.WriteLine("方法2:标准冒泡算法排序"); var count1 = 0; var count2 = 0; for (var i = 0; i < numbers.Length; i++) { for (var j = numbers.Length - 1; j > i; j--) { count1++; if (numbers[j] >= numbers[j - 1]) continue; count2++; var temp = numbers[j - 1]; numbers[j-1] = numbers[j]; numbers[j] = temp; } } Console.WriteLine($"结果:{string.Join(",", numbers)};循环次数:{count1};数据交换次数:{count2}"); }
运行一下,咱们会发现,调整后的代码循环的次数和效率跟初版如出一辙,确定的啊,由于中心思路没变,只是把冒泡的方向倒换了一下而已。blog
下面,咱们考虑这样一个数组:[1,0,2,3,5,4],使用第二版的代码:排序
第一次循环结束,数组结果为 [0,1,2,3,4,5],很神奇有没有,咱们不只把最小的0冒泡到第一位,顺带的最后两个元素也进行了排序。而后目测一下,数组已是正序排列了,也就是说这是咱们应该能够返回了有木有?图片
这也就是为何要从尾部往前冒泡的缘由。好了,能够优化的点出现了,接下来,请看第三版代码
public void Sort3(int[] numbers) { Console.WriteLine("方法2:优化版冒泡算法排序"); var count1 = 0; var count2 = 0; // 剩下的数据是否须要继续排序标志 // 由于冒泡排序是从后端两两交换,因此在某种时间点上,后端数据可能已是排列好的数据 // 如1,0,2,3,4这种状况,第一次循环后,把0交换到最前,结果为0,1,2,3,4 // 继续循环1,2,3,4,若是没有进行数据交换操做,说明已是排序好的,就不必继续了,直接跳出便可 var continueFlag = true; for (var i = 0; i < numbers.Length && continueFlag; i++) { for (var j = numbers.Length - 1; j > i; j--) { count1++; if (numbers[j] < numbers[j - 1]) { count2++; var temp = numbers[j - 1]; numbers[j - 1] = numbers[j]; numbers[j] = temp; // 一旦发生数据交换的操做,说明后面的数据并无排列好,这时须要继续循环 continueFlag = true; } else { // 没有发生数据交换,说明数据已是排列好的,这时能够跳出循环了 continueFlag = false; } } } Console.WriteLine($"结果:{string.Join(",", numbers)};循环次数:{count1};数据交换次数:{count2}"); }
下面,来进行一个测试:
var bubble = new BubbleSortSample(); var numbers = new int[] {3, 1, 2, 4, 6, 9, 5}; bubble.Sort1(numbers); Console.WriteLine(); numbers = new int[] { 3, 1, 2, 4, 6, 9, 5 }; bubble.Sort2(numbers); Console.WriteLine(); numbers = new int[] { 3, 1, 2, 4, 6, 9, 5 }; // 第一次 1,3,2,4,5,6,9,发生了交换,循环了6次 // 第二次 1,2,3,4,5,6,9,发生了交换,循环了5次 // 第三次 检查了一圈,循环了4次,没有发生交换,跳出 bubble.Sort3(numbers);
下面是运行结果:
冒泡排序是一种比较基础并且你们比较熟悉的算法,之前老是知其然而没有深究过,网上不少例子也是如初版代码同样简单实现演示一下了事,殊不知原来这么基础的算法也有这么多的门道在其中,因此说学海无涯...省略一万五千字鸡汤