算法卷轴(排序卷)[冒泡排序]

版权声明:火炉大印—曲奇喵 https://blog.csdn.net/afsya/article/details/84541051

定义

烈日炎炎的夏天,没有什么比一杯冰镇汽水更能让人平静了。

小气泡从杯底慢慢浮到表面的过程,就像冒泡排序的原理一样。

把每个元素当做一个气泡,每个元素与相邻的元素两两比较,根据大小来交换位置,一点点往数组的一端移动,最终得到一个有序的集合。

栗子

现在有 6 个数字组成的无序数列:
6, 4, 7, 2, 8, 5

  • 第一步:比较 6 和 4,发现 6 比 4 大,所以 6 和 4 交换位置。
    4, 6, 7, 2, 8, 5
  • 第二步:比较 6 和 7,发现 6 比 7 小,所以位置不变。
  • 第三步:比较 7 和 2,发现 7 比 2 大,所以 7 和 2 交换位置。
    4, 6, 2, 7, 8, 5
  • 第四步:比较 7 和 8,发现 7 比 8 小,所以位置不变。
  • 第五步:比较 8 和 5,发现 8 比 5 大,所以 8 和 5 交换位置。
    4, 6, 2, 7, 5, 8
    这时候,冒泡排序的第一轮就完成了,8 作为最大的元素,就像汽水里的气泡一样,浮到了最右侧。
    下面,我们来进行第二轮排序:
  • 第一步:比较 4 和 6,发现 4 比 6 小,所以位置不变。
  • 第二步:比较 6 和 2,发现 6 比 2 大,所以 6 和 2 交换位置。
    4, 2, 6, 7, 5, 8
  • 第三步:比较 6 和 7,发现 6 比 7 小,所以位置不变。
  • 第四步:比较 7 和 5,发现 7 比 5 大,所以 7 和 5 交换位置。
    4, 2, 6, 5, 7, 8
  • 第五步:比较 7 和 8,发现 7 比 8 小,所以位置不变。
    第二轮排序结束后,数列右侧的有序区已经有了两个元素:7、8。
    第三轮排序结束后:
    2, 4, 5, 6, 7, 8
    第四轮排序结束后(已经是有序的了,所以顺序没有变化):
    2, 4, 5, 6, 7, 8
    第五轮排序结束后(已经是有序的了,所以顺序没有变化):
    2, 4, 5, 6, 7, 8
    第六轮排序结束后(已经是有序的了,所以顺序没有变化):
    2, 4, 5, 6, 7, 8
    冒泡算法的每一轮排序,都要遍历所有元素,所以时间复杂度是 O(n²)。

实现

原始版

public static void sort(int[] array) {
	int temp = 0;
	for (int i = 0; i < array.length; i++) {
		for (int j = i + 1; j < array.length; j++) {
			if (array[i] > array[j]) {
			temp = array[i];
			array[i] = array[j];
			array[j] = temp;
			}
		}
	}
}

public static void main(String[] args) {
	int[] array = new int[] { 6, 4, 7, 2, 8, 5 };
	sort(array);
	// 结果:[2, 4, 5, 6, 7, 8]
	System.out.println(Arrays.toString(array));
}

Q:从上边的栗子来看,第三轮排序结束后,已经得到了有序的数列,但按照原始版的实现方式,还需要再进行 3 轮排序,怎样优化呢?
A:我们可以在判断出数列已经有序时,做出标记,让程序提前结束工作。

升级版

public static void sort(int[] array) {
	int temp = 0;
	for (int i = 0; i < array.length; i++) {
		// 每轮排序前,假设已经是有序的了,有序标记都是 true
		boolean isSorted = true;
		for (int j = i + 1; j < array.length; j++) {
			if (array[i] > array[j]) {
				temp = array[i];
				array[i] = array[j];
				array[j] = temp;
				// 如果需要调整,说明还不是有序数列,标记为 false
				isSorted = false;
			}
		}
		// 如果数列已经有序,不再进行下一轮排序
		if (isSorted) {
			break;
		}
	}
}

public static void main(String[] args) {
	int[] array = new int[] { 6, 4, 7, 2, 8, 5 };
	sort(array);
	// 结果:[2, 4, 5, 6, 7, 8]
	System.out.println(Arrays.toString(array));
}

Q:现在的冒泡排序实现方式,有序区域的长度跟排序轮数是一致的,但如果数列本来就存在有序的部分,在前几轮重复比较有序的元素是没有意义的,怎样优化呢?
A:我们可以在每一轮排序结束时,记录下最后一次交换元素的位置,这个位置就是无序数列的边界,相反区域就是有序数列了。

最终版

public static void sort(int[] array) {
	int temp = 0;
	// 最后一次交换的位置
	int lastExchange = 0;
	for (int i = 0; i < array.length; i++) {
		// 每轮排序前,假设已经是有序的了,有序标记都是 true
		boolean isSorted = true;
		for (int j = lastExchange; j < array.length; j++) {
			if (array[i] > array[j]) {
				temp = array[i];
				array[i] = array[j];
				array[j] = temp;
				// 如果需要调整,说明还不是有序数列,标记为 false
				isSorted = false;
				// 记录最后一次交换的位置
				lastExchange = j;
			}
		}
		// 如果数列已经有序,不再进行下一轮排序
		if (isSorted) {
			break;
		}
	}
}

public static void main(String[] args) {
	int[] array = new int[] { 6, 4, 7, 2, 8, 5 };
	sort(array);
	// 结果:[2, 4, 5, 6, 7, 8]
	System.out.println(Arrays.toString(array));
}

猜你喜欢

转载自blog.csdn.net/afsya/article/details/84541051