内容介绍
冒泡排序原理
冒泡排序(Bubble Sort)是一种交换排序,它的基本思想是:两两比较相邻的数据,大的日后放。java
你们必定看过水中的气泡,小气泡一点一点向上浮动变成大气泡。 算法
冒泡排序这个算法的名字由来是由于元素像水中的气泡冒泡同样,小的元素会通过交换慢慢“浮”到数列的一端。咱们来看冒泡排序的动画演示。 编程
冒泡排序的分析
通常没有特殊要求排序算法都是升序排序,小的在前,大的在后。数组由{6, 5, 4, 1, 3, 2}这6个无序元素组成。数组
冒泡排序交换过程图示(第一轮): 微信
第一次相邻的元素6和元素5比较,发现前面的元素大于后面的元素,交换位置。 优化
交换后 动画
第二次相邻的元素6和元素4比较,发现前面的元素大于后面的元素,交换位置。 code
交换后 blog
第三次相邻的元素6和元素1比较,发现前面的元素大于后面的元素,交换位置。 排序
交换后
第四次相邻的元素6和元素3比较,发现前面的元素大于后面的元素,交换位置。
交换后
第五次相邻的元素6和元素2比较,发现前面的元素大于后面的元素,交换位置。
交换后
通过第一轮的五次比较,元素6由数组的最前面冒泡到数组的最后面。最大的元素就在数组的最后面。注意观察,通过一轮比较只能将参数比较的数字中的最大的数字冒泡到右边,其他的仍是无序的。所以须要进行多轮比较才能将数组变成有序的。
冒泡排序交换过程图示(第二轮):
通过第二轮的四次比较,元素5由数组的最前面冒泡到数组的倒数第二的位置。大的元素就在数组的后面。数组最后面的元素5和元素6能够比较也能够不比较,对结果没有影响。到时候咱们写代码的时候注意一下便可。
冒泡排序交换过程图示(第三轮):
交换过程和前两轮分析的相似,咱们省略过程,最终第三轮交换后的结果:
冒泡排序交换过程图示(第四轮):
交换过程和前两轮分析的相似,咱们省略过程,最终第四轮交换后的结果:
冒泡排序交换过程图示(第五轮):
第五轮比价元素1和元素2,这两个元素自己已经有序了,因此没有变化,最终第五轮交换后的结果和第四轮同样:
到此为止,全部元素都是有序的了,这就是冒泡排序的总体过程。
冒泡排序代码编写
咱们发现每一轮都须要从索引0开始,相邻元素两比较,须要使用一个循环(内循环)控制每轮的比较元素。另外数组6个元素须要通过五轮比较,也须要使用一个循环(外循环)控制比较的轮数,并且比较的轮数是元素的数量-1。
public class BubbleSortTest { public static void main(String[] args) { int[] arr = new int[]{6, 5, 4, 1, 3, 2}; System.out.println("排序前数组" + Arrays.toString(arr)); bubbleSort1(arr); } // 冒泡排序 public static void bubbleSort1(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { // 外循环控制比较的轮数,轮数是元素的数量-1 for (int j = 0; j < arr.length - 1 - i; j++) { // 内循环控制每轮比较的元素,轮数越大,比较的次数越少 if (arr[j] > arr[j+1]) { // i和i+1索引比较,也就是相邻元素比较,大的日后放 System.out.print("\t元素 " + arr[j] + " 和 " + arr[j+1] + " 比较, "); int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; System.out.println("比较后" + Arrays.toString(arr)); } } System.out.println("排序" + (i+1) + "轮后: " + Arrays.toString(arr)); } } }
运行效果以下:
排序前数组[6, 5, 4, 1, 3, 2] 元素 6 和 5 比较, 比较后[5, 6, 4, 1, 3, 2] 元素 6 和 4 比较, 比较后[5, 4, 6, 1, 3, 2] 元素 6 和 1 比较, 比较后[5, 4, 1, 6, 3, 2] 元素 6 和 3 比较, 比较后[5, 4, 1, 3, 6, 2] 元素 6 和 2 比较, 比较后[5, 4, 1, 3, 2, 6] 排序1轮后: [5, 4, 1, 3, 2, 6] 元素 5 和 4 比较, 比较后[4, 5, 1, 3, 2, 6] 元素 5 和 1 比较, 比较后[4, 1, 5, 3, 2, 6] 元素 5 和 3 比较, 比较后[4, 1, 3, 5, 2, 6] 元素 5 和 2 比较, 比较后[4, 1, 3, 2, 5, 6] 排序2轮后: [4, 1, 3, 2, 5, 6] 元素 4 和 1 比较, 比较后[1, 4, 3, 2, 5, 6] 元素 4 和 3 比较, 比较后[1, 3, 4, 2, 5, 6] 元素 4 和 2 比较, 比较后[1, 3, 2, 4, 5, 6] 排序3轮后: [1, 3, 2, 4, 5, 6] 元素 3 和 2 比较, 比较后[1, 2, 3, 4, 5, 6] 排序4轮后: [1, 2, 3, 4, 5, 6] 排序5轮后: [1, 2, 3, 4, 5, 6]
这是最原始的冒泡排序,该排序算法的每一轮要遍历全部元素,而且轮数和元素数量只少1,时间复杂度: 最优时间复杂度:O(n^2) (即便元素有序仍是须要进行比较) 最坏时间复杂度:O(n^2) 稳定性:稳定
这个算法的效率是很是低的。
冒泡排序代码优化1
优化1: 若是有一轮发现没有须要排序的,说明已经有序了,能够不用再遍历比较了。以下图所示,第一轮比较事后,第二轮比较时发现元素已经所有是有序的,直接退出就没有必要再进行后面第三,第四,五轮的比较了。
代码以下:
public class BubbleSortTest2 { public static void main(String[] args) { int[] arr = new int[]{1, 3, 2, 4, 5, 6}; System.out.println("排序前数组" + Arrays.toString(arr)); bubbleSort2(arr); } // 优化1:若是有一轮发现没有须要排序的,说明已经有序了.能够不用再遍历比较了 public static void bubbleSort2(int[] arr) { // 0.132s for (int i = 0; i < arr.length - 1; i++) { boolean sorted = true; // 用于标记数组是否有序,若是这轮一个元素都没有交换说明有序,后面几轮不须要在排序了 for (int j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j+1]) { int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; sorted = false; // 有元素须要交换,说明数组仍是无序的. } } if (sorted) { System.out.println("已经有序了, length = " + arr.length + ", 第 " + (i+1) + "轮"); break; } } } }
运行效果以下:
排序前数组[1, 3, 2, 4, 5, 6] 已经有序了, length = 6, 第 2轮
利用布尔变量sorted做为标记。若是在本轮排序中,没有元素交换,说明数组已经有序,直接跳出大循环。通过此次优化后,若是数组已经有顺序了,能够减小比较的轮数。
冒泡排序代码优化2
优化2: 问题是在于j<array.length– i– 1。每轮都会比较到array.length– i– 1这个位置,若是后面已是有序的就不须要比较,下一轮只须要比较到上一轮的最后一次交换的小值那个地方便可,减小每轮的比较次数。
优化前第二轮须要比较到索引4的位置,优化后第二轮只须要比较到上次最后比较的那个位置,也就是lastPosition的位置
代码以下:
public class BubbleSortTest2 { public static void main(String[] args) { int[] arr = new int[]{3, 2, 1, 4, 5, 6, 7}; System.out.println("排序前数组" + Arrays.toString(arr)); bubbleSort3(arr); } // 优化2:问题是在于j<array.length– i– 1。若是后面已是有序的就不须要比较 // 下一轮只须要比较到上一轮的最后一次交换的小值那个地方便可 public static void bubbleSort3(int[] arr) { // 0.132s int lastPosition = 0; // 记录这一轮最后一次比较的较小那个值,下一轮就比较到这里 int len = arr.length - 1; // len表示下一轮要比较到哪里?后面会让len = lastPosition for (int i = 0; i < arr.length - 1; i++) { boolean sorted = true; // 用于标记数组是否有序,若是这轮一个元素都没有交换说明有序,后面几轮不须要在排序了 for (int j = 0; j < len; j++) { if (arr[j] > arr[j+1]) { int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; sorted = false; // 有元素须要交换,说明数组仍是无序的. lastPosition = j; // 记录这一轮最后一次比较的较小那个值,下一轮就比较到这里 } else { System.out.println("第 " + i + " 轮的第 " + j + " 次不须要交换"); } } len = lastPosition; if (sorted) { System.out.println("已经有序了, length = " + arr.length + ", i = " + i); break; } } } }
冒泡排序复杂度
这是最原始的冒泡排序,该排序算法的每一轮要遍历全部元素,而且轮数和元素数量只少1,时间复杂度:
- 最优时间复杂度:O(n) 当数据自己已经有序如:{1, 2, 3, 4, 5, 6},咱们作了优化,比较一轮就结束。
- 最坏时间复杂度:O(n^2) 当数据自己是倒序的如:{6, 5, 4, 3, 2, 1},总执行次数为n^2-n。
- 平均时间复杂度:O(n^2)。
- 稳定性:稳定
总结
冒泡排序是一种交换排序,它的基本思想是:两两比较相邻的数据,大的日后放。使用嵌套循环来实现冒泡排序,外循环控制比较的轮数,内循环控制每轮比较的次数。 冒泡排序能够进行两个优化:
- 若是有一轮发现没有须要排序的,说明已经有序了,能够不用再遍历比较了,减小比较轮数。
- 每轮都会比较到array.length– i– 1这个位置,若是后面已是有序的就不须要比较,下一轮只须要比较到上一轮的最后一次交换的小值那个地方便可,减小每轮的比较次数。
原创文章和动画制做真心不易,您的点赞就是最大的支持! 想了解更多文章请关注微信公众号:表哥动画学编程