冒泡排序总结

本文内容和代码均来自于《漫画算法》,小灰和大黄的对话,非常有趣味的一本书。现理论结合实践,做一下测试。

private static final int LEN = 20000;

	// 第一版
	void bubbleV1(int[] arr) {
		for (int i = 0; i < arr.length - 1; i++) {
			for (int j = 0; j < arr.length - 1 - i; j++) {
				int temp = 0;
				if (arr[j] > arr[j + 1]) {
					temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}

			}
			log("第" + (i + 1) + "轮");
			log(Arrays.toString(arr));
		}
	}

	/**
	 * 第一版如果元素经过小于arr.length-1轮比较已经变的有序,依然会进行剩下的轮数比较,
	 * 为了克服这个缺点,增加一个判断数组是否已经有序的boolean变量。 第二版
	 * 
	 * @param arr
	 */
	void bubbleV2(int[] arr) {
		for (int i = 0; i < arr.length - 1; i++) {
			boolean isSorted = true;
			for (int j = 0; j < arr.length - 1 - i; j++) {
				int temp = 0;
				log("compare " + arr[j] + " with " + arr[j + 1]);
				if (arr[j] > arr[j + 1]) {
					temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
					isSorted = false;
				}

			}
			if (isSorted) {
				break;
			}
			log("第" + (i + 1) + "轮");
			log(Arrays.toString(arr));
		}
	}

	/**
	 * 第三版 [3,2,1,4,5,6,7,8] [2,1,3,4,5,6,7,8] [1,2,3,4,5,6,7,8]
	 * 假如这样的初始数组,从4开始的元素已经是有序的了,但是还是白白的比较了许多次。 是一个需要优化的点。 这个问题的关键点在于对数组中有序区的界定。
	 * 按照现有逻辑,有序区的长度和,排序的轮数是相等的。如第一轮排序过后的有序区长度是1, 第二轮排序过后的有序区长度是2......
	 * 实际上有序区的长度可能大于这个长度,上述例子中,在第二轮排序时,后面的5个元素已经属于有序区了。 因此后面的多次元素比较是没有意义的。
	 * 避免方法:在每一轮排序后,记录下最后一次元素交换的位置,该位置即为无需数列的边界,再往后就是有序区了。
	 * 
	 * @param arr
	 */
	void bubbleV3(int[] arr) {
		int lastExchangedIndex = 0;
		int sortBorder = arr.length - 1;
		for (int i = 0; i < arr.length - 1; i++) {
			boolean isSorted = true;
			for (int j = 0; j < sortBorder; j++) {
				int temp = 0;
				log("compare " + arr[j] + " with " + arr[j + 1]);
				if (arr[j] > arr[j + 1]) {
					temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
					isSorted = false;
					lastExchangedIndex = j;
				}

			}
			sortBorder = lastExchangedIndex;
			log("border:" + sortBorder);
			if (isSorted) {
				break;
			}
			log("第" + (i + 1) + "轮");
			log(Arrays.toString(arr));
		}
	}

	/**
	 * 第四版(鸡尾酒排序) [2,3,4,5,6,7,8,1] 如上例子,前面三种版本排序方法需要排7轮,然而大部分的数据都是有序的。
	 * 为了克服这个缺点。
	 * 
	 * @param arr
	 */
	void bubbleV4(int[] arr) {
		int temp = 0;
		for (int i = 0; i < arr.length / 2; i++) {
			// 有序标记,每一轮初始值都是true
			boolean isSorted = true;
			// 奇数轮,从左往右比较和交换
			for (int j = i; j < arr.length - 1 - i; j++) {
				log("compare " + arr[j] + " with " + arr[j + 1]);
				if (arr[j] > arr[j + 1]) {
					temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
					isSorted = false;
				}

			}
			if (isSorted) {
				break;
			}
			log("第" + (i + 1) + "轮");
			log(Arrays.toString(arr));

			// 偶数轮,从右往左比较和交换
			isSorted = true;
			for (int k = arr.length - 1 - i; k > i; k--) {
				if (arr[k] < arr[k - 1]) {
					temp = arr[k];
					arr[k] = arr[k - 1];
					arr[k - 1] = temp;
					isSorted = false;
				}
			}
			if (isSorted) {
				break;
			}
			log("第" + (i + 2) + "轮");
			log(Arrays.toString(arr));
		}
	}

	@Test
	public void test() {
		// int[] arr={3,2,1,4,5,6,7,8};
		int[] arr = { 2, 3, 4, 5, 6, 7, 8, 1 };
		// int[] arr={5,8,6,3,9,2,1,7};
		// int[] arr={25, 58, 29, 3, 21, 3, 60, 46, 79, 15};
		// bubbleV1(arr);
		// bubbleV2(arr);
		// bubbleV3(arr);
		bubbleV4(arr);
		log(Arrays.toString(arr));

	}

	@Test
	public void test2() {
		int[] arr2 = new int[LEN];
		Random random = new Random();
		for (int i = 0; i < LEN; i++) {
			arr2[i] = random.nextInt(100);
		}
		// log(Arrays.toString(arr2));

		long begin, end;

		begin = System.currentTimeMillis();
		bubbleV1(arr2);
		end = System.currentTimeMillis();
		System.out.println(end - begin);

		begin = System.currentTimeMillis();
		bubbleV2(arr2);
		end = System.currentTimeMillis();
		System.out.println(end - begin);

		begin = System.currentTimeMillis();
		bubbleV3(arr2);
		end = System.currentTimeMillis();
		System.out.println(end - begin);

		begin = System.currentTimeMillis();
		bubbleV4(arr2);
		end = System.currentTimeMillis();
		System.out.println(end - begin);

	}

	void log(String x) {
		if (false) {
			System.out.println(x);
		}
	}

分别用1万个元素和2万个整型数据做测试,结果如下

方法版本数组长度(万)耗时(ms)
112833
213
312
412
1210475
224
323
423
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值