冒泡排序(英语:Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
(图片来源于维基百科)
简单的冒泡排序算法
第 1 轮比较,每次从数组最前面往后面比较,较大的元素会往后面交换,一直比较到最后数组最后一位,那么数组第 (n - 1) 位就是数组里最大的元素;第 2 轮比较,一直比较到第 (n - 2) 位,n 是数组长度,数组里面第 2 大的元素就会确定在数组 (n - 2) 的位置;以此类推。这个过程就像水底里的气泡慢慢往上浮出水面,离水底越远,水压越小,气泡上升过程中变得越来越大。
1 //简单的冒泡排序 2 public static void bubbleSorts1(int[] a, int n) { 3 int i, j; 4 int count = 0;//排序次数 5 for (i = 0; i < n; i++) {//表示n次排序过程 6 for (j = 1; j < n - i; j++) {//从第一个往后面遍历并比较,直到排好序的那些数之前,n-i位是放排好的数 7 count++; 8 if (a[j - 1] > a[j]) {//表示前面的数字大于当前数字时交换 9 int tmp; 10 tmp = a[j - 1]; 11 a[j - 1] = a[j]; 12 a[j] = tmp; 13 } 14 } 15 } 16 System.out.println("普通冒泡排序比较次数:" + count); 17 }
复杂度分析:每次循环从最低位往最高位移动元素,每轮循环下来后,最大的数被挪到了最后面(已经确定这个元素在数组里是最大的)。代码中使用了两个 for 循环,时间复杂度为 O(n²);交换元素使用到一个变量,因而空间复杂度为 O(1)。
冒泡排序的优化
当一轮循环的比较下来后,数组中没有发生元素的交换,则说明这些需要排序的元素是符合排序顺序的。如果一轮下来没有发生任何元素交换,那么继续下一轮循环是没有意义的,因为元素已经确定是排好序的,所以要停止算法继续比较。
优化的实现:即使对数组进行排序,上述函数也始终运行 O(n2) 时间。如果内部循环未引起任何交换,则可以通过停止算法来对其进行优化。
1 //设有标示位的冒泡排序 2 public static void bubbleSorts2(int[] a, int n) { 3 int i, j = n; 4 int count = 0;//排序次数 5 boolean flag = true;//标志位用于表示当前趟有没有交换过,没有则是已经排好序的,则没有必要排序下去 6 while (flag) { 7 flag = false;//每轮设置为未排好序 8 for (i = 1; i < j; i++) { 9 count++; 10 if (a[i - 1] > a[i]) { 11 int tmp; 12 tmp = a[i - 1]; 13 a[i - 1] = a[i]; 14 a[i] = tmp; 15 flag = true;//有交换,数组中有小到大的顺序未知,应在遍历一遍 16 } 17 } 18 j--;//每遍历一轮,就把最大的数放在了数组后面 19 } 20 System.out.println("带标志位的冒泡排序比较次数:" + count); 21 }
最坏情况和平均情况下的时间复杂度:O(n2),最坏的情况是对反向排好序的数组进行排序。
最佳情况时间复杂度:O(n),最好情况是对排好序的数组进行排序。
空间复杂度:O(1)
算法稳定性:稳定。
(所谓稳定就是说相同的元素在其原先的相对位置不变,比如说两个数字 1 和 1(就像每个人有身份证号),排好序后,前面那个 1 (人)依旧在后面那个 1(人)的前面。)
测试方法
1 public static void main(String[] args) { 2 int[] arr = {1, 1, 2, 0, 9, 3, 12, 7, 8, 3, 4, 65, 22}; 3 int[] arr1 = {1, 1, 3, 2, 9}; 4 BubbleSort.bubbleSorts2(arr1, arr1.length); 5 6 for (int i : arr1) { 7 System.out.print(i + " "); 8 } 9 }