本文根据《大话数据结构》一书,实现了Java版的冒泡排序

更多:数据结构与算法合集

基本概念

  基本思想:将相邻的元素两两比较,根据大小关系交换位置,直到完成排序。

  对n个数组成的无序数列,进行n轮排序,每轮按两两比较的方法找出最小(或最大)的一个。下图表示某数列的第一轮排序。

下面为交换元素的swap()方法代码,后面代码中将直接使用。

  1. public void swap(int[] a, int i, int j) {
  2. int temp;
  3. temp = a[j];
  4. a[j] = a[i];
  5. a[i] = temp;
  6. }

  

初级版本

根据基本思想,可以写出初级版本的冒泡排序如下:

  1. public void bubbleSort0(int[] a) {
  2. if(a==null) return;
  3. // 代表第i轮排序
  4. for (int i = 0; i < a.length; i++) {
  5. for (int j = a.length - 1; j > i; j--) {
  6. if (a[j] < a[j - 1]) {
  7. swap(a, i, j);
  8. }
  9. }
  10. }
  11. }

    

第一次优化版本

  当数据基本有序时,可能前几轮循环就完成了排序,后面的循环就没有必要继续进行了,如下图所示:

  对这种情况,可以在代码中增加一个标记,用于标记每轮循环时代码是否已经有序,在每轮循环开始前,如果有序的话就没有必要继续进行比较了。具体Java代码如下:

  1. public void bubbleSort1(int[] a) {
  2. if(a==null) return;
  3. boolean isSorted = false; // false代表数据无序,需要排序
  4. for (int i = 0; i < a.length && !isSorted; i++) { // 数据无序时还要继续循环
  5. isSorted = true; // 假设这轮循环开始时已经有序
  6. for (int j = a.length - 1; j > i; j--) {
  7. if (a[j] < a[j - 1]) {
  8. swap(a, i, j);
  9. isSorted = false; // 有发生交换,说明这轮循环还是无序的
  10. }
  11. }
  12. }
  13. }

    

第二次优化版本

  当数列的前半部分有序而后半部分无序时,每轮循环没必要再对有序部分进行排序,例如,数列为{1,2,3,4,9,5,8,7}时,在一次循环后知道1,2,3,4已经有序,后面的循环就没必要对这些数字进行排序了。

  此时,关键点在于对有序区的界定:如果知道有序区的边界,那么每次循环就只需要比较到该边界即可。在每次循环的最后,记录下最后一次元素交换的位置,该位置就是有序区的边界了。具体Java代码如下:

  1. public void bubbleSort2(int[] a) {
  2. if(a==null) return;
  3. int lastExchangeIndex = 0; // 用于记录每轮循环最后一次交换的位置
  4. int sortBorder = 0; // 有序数组的边界,每次比较只要比较到这里就可以
  5. boolean isSorted = false;
  6. for (int i = 0; i < a.length && !isSorted; i++) {
  7. isSorted = true;
  8. for (int j = a.length - 1; j > sortBorder; j--) {
  9. if (a[j] < a[j - 1]) {
  10. swap(a, i, j);
  11. isSorted = false;
  12. lastExchangeIndex = j; // 本轮最后一次交换位置(不断更新)
  13. }
  14. }
  15. sortBorder = lastExchangeIndex; // 边界更新为最后一次交换位置
  16. }
  17. }

    

完整Java代码

(含测试代码)

  1. import java.util.Arrays;
  2.  
  3. /**
  4. *
  5. * @Description 冒泡排序(从小到大)
  6. *
  7. * @author yongh
  8. * @date 2018年9月13日 下午3:21:38
  9. */
  10. public class BubbleSort {
  11.  
  12. /**
  13. * 初级版本
  14. */
  15. public void bubbleSort0(int[] a) {
  16. if(a==null) return;
  17. // 代表第i轮排序
  18. for (int i = 0; i < a.length; i++) {
  19. for (int j = a.length - 1; j > i; j--) {
  20. if (a[j] < a[j - 1]) {
  21. swap(a, i, j);
  22. }
  23. }
  24. }
  25. }
  26.  
  27. /**
  28. * 优化版本
  29. * 添加一个标记isSorted
  30. */
  31. public void bubbleSort1(int[] a) {
  32. if(a==null) return;
  33. boolean isSorted = false; // false代表数据无序,需要排序
  34. for (int i = 0; i < a.length && !isSorted; i++) { // 数据无序时还要继续循环
  35. isSorted = true; // 假设这轮循环开始时已经有序
  36. for (int j = a.length - 1; j > i; j--) {
  37. if (a[j] < a[j - 1]) {
  38. swap(a, i, j);
  39. isSorted = false; // 有发生交换,说明这轮循环还是无序的
  40. }
  41. }
  42. }
  43. }
  44.  
  45. /**
  46. * 进一步优化版本
  47. */
  48. public void bubbleSort2(int[] a) {
  49. if(a==null) return;
  50. int lastExchangeIndex = 0; // 用于记录每轮循环最后一次交换的位置
  51. int sortBorder = 0; // 有序数组的边界,每次比较只要比较到这里就可以
  52. boolean isSorted = false;
  53. for (int i = 0; i < a.length && !isSorted; i++) {
  54. isSorted = true;
  55. for (int j = a.length - 1; j > sortBorder; j--) {
  56. if (a[j] < a[j - 1]) {
  57. swap(a, i, j);
  58. isSorted = false;
  59. lastExchangeIndex = j; // 本轮最后一次交换位置(不断更新)
  60. }
  61. }
  62. sortBorder = lastExchangeIndex; // 边界更新为最后一次交换位置
  63. }
  64. }
  65.  
  66. /**
  67. * 交换代码
  68. */
  69. public void swap(int[] a, int i, int j) {
  70. int temp;
  71. temp = a[j];
  72. a[j] = a[i];
  73. a[i] = temp;
  74. }
  75.  
  76. //=========测试代码=======
  77. public void test1() {
  78. int[] a = null;
  79. bubbleSort2(a);
  80. System.out.println(Arrays.toString(a));
  81. }
  82.  
  83. public void test2() {
  84. int[] a = {};
  85. bubbleSort2(a);
  86. System.out.println(Arrays.toString(a));
  87. }
  88.  
  89. public void test3() {
  90. int[] a = { 1 };
  91. bubbleSort2(a);
  92. System.out.println(Arrays.toString(a));
  93. }
  94.  
  95. public void test4() {
  96. int[] a = { 3, 3, 3, 3, 3 };
  97. bubbleSort2(a);
  98. System.out.println(Arrays.toString(a));
  99. }
  100.  
  101. public void test5() {
  102. int[] a = { -3, 6, 3, 1, 3, 7, 5, 6, 2 };
  103. bubbleSort2(a);
  104. System.out.println(Arrays.toString(a));
  105. }
  106.  
  107. public static void main(String[] args) {;
  108. BubbleSort demo = new BubbleSort();
  109. demo.test1();
  110. demo.test2();
  111. demo.test3();
  112. demo.test4();
  113. demo.test5();
  114. }
  115. }

    

  1. null
  2. []
  3. []
  4. [, , , , ]
  5. [, , -, , , , , , ]

BubbleSort

总结

  冒泡排序原理近似于气泡在水里慢慢上浮到水面上,实现容易,但也有改进的空间,

  改进1:若前几轮已经有序,则后面就没必要继续比较了,因此增加一个isSorted标记,对每轮是否有序进行标记。

  改进2:一部分有序,则没必要继续对有序区比较,增加一个sortBorder来定义有序区边界,每次比较到该边界即可。该边界由每轮循环中最后一次元素交换的位置得到。

  时间复杂度:O(n^2)

更多:数据结构与算法合集

【Java】 大话数据结构(14) 排序算法(1) (冒泡排序及其优化)的更多相关文章

  1. 【Java】 大话数据结构(15) 排序算法(2) (快速排序及其优化)

    本文根据<大话数据结构>一书,实现了Java版的快速排序. 更多:数据结构与算法合集 基本概念 基本思想:在每轮排序中,选取一个基准元素,其他元素中比基准元素小的排到数列的一边,大的排到数 ...

  2. 【Java】 大话数据结构(16) 排序算法(3) (堆排序)

    本文根据<大话数据结构>一书,实现了Java版的堆排序. 更多:数据结构与算法合集 基本概念 堆排序种的堆指的是数据结构中的堆,而不是内存模型中的堆. 堆:可以看成一棵完全二叉树,每个结点 ...

  3. 【Java】 大话数据结构(17) 排序算法(4) (归并排序)

    本文根据<大话数据结构>一书,实现了Java版的归并排序. 更多:数据结构与算法合集 基本概念 归并排序:将n个记录的序列看出n个有序的子序列,每个子序列长度为1,然后不断两两排序归并,直 ...

  4. 【Java】 大话数据结构(18) 排序算法(5) (直接插入排序)

    本文根据<大话数据结构>一书,实现了Java版的直接插入排序. 更多:数据结构与算法合集 基本概念 直接插入排序思路:类似扑克牌的排序过程,从左到右依次遍历,如果遇到一个数小于前一个数,则 ...

  5. Java常用的经典排序算法:冒泡排序与选择排序

     一.冒泡排序                             冒泡排序(Bubble Sort)是一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为 ...

  6. Java常见排序算法之冒泡排序

    在学习算法的过程中,我们难免会接触很多和排序相关的算法.总而言之,对于任何编程人员来说,基本的排序算法是必须要掌握的. 从今天开始,我们将要进行基本的排序算法的讲解.Are you ready?Let ...

  7. java排序算法之冒泡排序(Bubble Sort)

    java排序算法之冒泡排序(Bubble Sort) 原理:比较两个相邻的元素,将值大的元素交换至右端. 思路:依次比较相邻的两个数,将小数放在前面,大数放在后面.即在第一趟:首先比较第1个和第2个数 ...

  8. 【Java】 大话数据结构(11) 查找算法(2)(二叉排序树/二叉搜索树)

    本文根据<大话数据结构>一书,实现了Java版的二叉排序树/二叉搜索树. 二叉排序树介绍 在上篇博客中,顺序表的插入和删除效率还可以,但查找效率很低:而有序线性表中,可以使用折半.插值.斐 ...

  9. 排序算法之冒泡排序Java实现

    排序算法之冒泡排序 舞蹈演示排序: 冒泡排序: http://t.cn/hrf58M 希尔排序:http://t.cn/hrosvb  选择排序:http://t.cn/hros6e  插入排序:ht ...

随机推荐

  1. Problem C: 多线程 解题报告

    Problem C: 多线程 Description 多线程是一种常见的加速手段,利用多个线程同时处理不同的任务可以一定程度上减少总耗时,达到提高效率的目的.然而,多个线程间的执行顺序是完全不可控的, ...

  2. [SCOI2014]方伯伯的商场之旅

    Description 方伯伯有一天去参加一个商场举办的游戏.商场派了一些工作人员排成一行.每个人面前有几堆石子.说来也巧,位置在 i 的人面前的第 j 堆的石子的数量,刚好是 i 写成 K 进制后的 ...

  3. (转) eclipse项目中.classpath文件详解

    背景:对于java项目中.classpath文件中的相关定义一直不是很了解,有必要进行深入的学习. 1 前言 在使用eclipse或者myeclipse进行Java项目开发的时候,每个project( ...

  4. StringEscapeUtils的常用使用,防止SQL注入及XSS注入

    StringEscapeUtils的常用使用,防止SQL注入及XSS注入 2017年10月20日 11:29:44 小狮王 阅读数:8974   版权声明:本文为博主原创文章,转载请注明出处. htt ...

  5. Eclipse配置Maven的一些问题

    问题1:连接私服 build项目非常缓慢 配置好本地的setting文件后,发现build非常缓慢,照显示的进度,可能要一天才会build后一个项目,同事指导解决方法如下: MyEclipse2017 ...

  6. 图像处理之生成ColorBar

    1 colorBar介绍 colorBar主要是指一些图像处理中使用的常见纯色或者渐变色条.colorBar用途可作为测试样图来验证某些图像算法的效果,从而避免图像内容或者硬件对图像的干扰,使图像算法 ...

  7. 用rem来做响应式开发(转)

    由于最近在做公司移动项目的重构,因为要实现响应式的开发,所以大量使用到了rem的单位,觉得这个单位有点意思.但是现在貌似用他的人很少.上一篇文章我分享了淘宝写的一篇rem的介绍,介绍的非常全面,但是他 ...

  8. Java基础-SSM之mybatis的统计函数和分页查询

    Java基础-SSM之mybatis的统计函数和分页查询 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.

  9. linq中let关键字学习

    linq中let关键字就是对子查询的一个别名,let子句用于在查询中添加一个新的局部变量,使其在后面的查询中可见. linq中let关键字实例 1.传统下的子查询与LET关键字的区别     C# 代 ...

  10. iOS 6 & iOS 7 的适配笔记

    iOS 6 & iOS 7 的适配 场景1: 没有NavigationController,同时根视图是UIView- (void)viewWillLayoutSubviews{ if ([[ ...