冒泡排序(Java)
文章汇总归纳整理于:算法竞赛学习之路[Java版]
默认排序后的数据,从小到大进行排列
冒泡排序的基本思想
- 从后往前(或从前往后)两两比较相邻元素的值,若为逆序(即A[i-1]>A[i]),则交换它们,直到序列比较完。
- 我们称这次为第一趟冒泡,结果是将最小的元素交换到待排序列的第一个位置(或将最大的元素交换到待排序列的最后一个位置),关键字最小的元素如气泡一般逐渐往上“漂浮”直至“水面”(或关键字最大的元素如石头一般下沉至水底)。
- 下一趟冒泡时,前一趟确定的最小元素不再参与比较,每趟冒泡的结果是把序列中的最小元素(或最大元素)放到了序列的最终位置……
- 这样最多做 n-1 趟冒泡就能把所有元素排好序。
冒泡排序的过程
- 执行到第四趟冒泡时,未排序数据只剩一个,无需再继续执行冒泡,即已经完成排序
冒泡排序的代码实现
这里以 P1116 车厢重组 为例,实现冒泡排序
// package 冒泡排序;import java.io.*;public class Main {// 快读快写static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));static PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));public static void main(String[] args) {// 读取数据int n = readInt();int[] nums = new int[n]; // 存放初始的车厢顺序的数组// 读取初始的车厢顺序for ( int i=0; i<n; i++ ) {nums[i] = readInt();}int ans = 0; // 记录交换的次数// 冒泡排序for ( int i=1; i<n; i++ ) { // 最多 n-1 趟冒泡for ( int j=n-1; j>=i; j-- ) { // 从后向前遍历,将未排序数据中最小的数冒泡到最上面if ( nums[j] < nums[j-1] ) { // 逆序,交换数据ans++; // 交换次数++// 交换数据int t = nums[j];nums[j] = nums[j-1];nums[j-1] = t;}}}// for ( int i=0; i<n; i++ ) out.println(nums[i]);out.print(ans); // 输出结果out.flush();}// 读取整数static int readInt() {try {in.nextToken();} catch (IOException e) {e.printStackTrace();}return (int) in.nval;}
}
冒泡排序的优化
- 在冒泡排序过程中,如果存在某趟的冒泡中,没有发生数据的交换,则说明在本趟冒泡排序之前,数据已经有序了,无需再进行后面的排序。
- 因此我们可以利用这个对冒泡排序进行优化,在每趟排序之前,创建一个标记变量,用于记录本趟是否发生数据交换,如果没有发生数据交换,则无需再进行后续的排序,直接结束排序。
// package 冒泡排序;import java.io.*;public class Main {static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));static PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));public static void main(String[] args) {// 读取数据int n = readInt();int[] nums = new int[n]; // 存放初始的车厢顺序的数组for ( int i=0; i<n; i++ ) {nums[i] = readInt();}int ans = 0; // 记录交换的次数// 冒泡排序for ( int i=1; i<n; i++ ) { // 最多 n-1 趟冒泡// 标记本趟是否有发生数据的交换,如果没有则说明数据已经有序,无需后序的排序boolean flag = false; for ( int j=n-1; j>=i; j-- ) { // 将未排序数据中最小的数冒泡到最上面if ( nums[j] < nums[j-1] ) { // 逆序,交换数据ans++; // 交换次数++// 交换数据int t = nums[j];nums[j] = nums[j-1];nums[j-1] = t;flag = true; // 本趟冒泡有发生数据的交换}}if ( !flag ) break; // 如果本趟未发生数据交换则说明数据已经有序,无需后序的排序}// for ( int i=0; i<n; i++ ) out.println(nums[i]);out.print(ans);out.flush();}static int readInt() {try {in.nextToken();} catch (IOException e) {e.printStackTrace();}return (int) in.nval;}
}
冒泡排序的性能分析
- 空间效率:仅使用了常数个辅助单元,因而空间复杂度为O(1)。
- 时间效率:
- 当初始序列有序时,显然第一趟冒泡后 flag 依然为 false (本趟没有元素交换),从而直接跳出循环,比较次数为 n-1,移动次数为0,从而最好情况下的时间复杂度为O(n)
- 当初始序列为逆序时,需要进行 n-1 趟排序,第 i 趟排序要进行 n-i 次关键字的比较,而且每次比较后都必须交换元素位置。这种情况下,
- 从而,最坏情况下的时间复杂度为O(n2),平均时间复杂度为O(n2)。
- 没有优化的冒泡排序的平均时间复杂度为O(n2)
- 稳定性:由于 i>j 且 A[i]=A[j] 时,不会发生交换,因此冒泡排序是一种稳定的排序方法。