【排序算法】:冒泡排序及其三种优化(Java)

冒泡排序

       冒泡排序是一种简单的排序算法,它是重复地遍历要排序的元素列,依次对两个相邻的元素进行比较,如果他们的顺序(如从大到小、首字母从A到Z)相反则将他们进行交换,这样每一趟遍历会将最大或者最小的元素移动到最右端,如果一趟走访没有相邻的元素需要交换,则说该元素列已经有序,有n个元素的元素列最多需要走访n-1趟。

实现思路 

  1. 比较相邻的两个元素,如果第一个比第二个大(小),则交换他们位置。
  2. 对每一对相邻元素做上述的工作,从开始第一对到结尾的最后一对,则最后的元素应该会是最大(最小)的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每趟对越来越少的元素重复上面的步骤,直到没有任何一对元素需要交换。

举例说明

待排数组:{21,10,52,4,9,2}


第一趟排序过程:

           第一次比较:21和10比较,21比10大,交换位置,得:10,21,52,4,9,2

           第二次比较:21和52比较,21比52小,不用交换位置,得:10,21,52,4,9,2

           第三次比较:52和4比较,52比4大,交换位置,得:10,21,4,52,9,2

           第四次比较:52和9比较,52比9大,交换位置,得:10,21,4,9,52,2

           第五次比较:52和2比较,52比2大,交换位置,得:10,21,4,9,2,52

第一趟排序结束,排序结果:10,21,4,9,2,52


依照上面的操作方法,可得到:

第一趟排序结果:10,21,4,9,2,52

第二趟排序结果:10,4,9,2,21,52

第三趟排序结果:4,9,2,10,21,52

第四趟排序结果:4,2,9,10,21,52

第五趟排序结果:2,4,9,10,21,52


最终排序结果:2,4,9,10,21,52

代码实现

冒泡排序一般实现

/**
	 * 最简单的实现方法
	 * @param a
	 */
	public static void bubbleSort(int[] a) {
		int n = a.length;
		for (int i = 1; i < n; i++) { // 一共n-1趟排序
			for (int j = 0; j < n - i; j++) { // 第i趟排序从第0个元素到第n-i个元素
				if (a[j] > a[j + 1]) {
					int temp;
					temp = a[j];
					a[j] = a[j + 1];
					a[j + 1] = temp;
				}
			}
		}
	}

优化一: 

对于一些连片有序而整体无序的序列,如{2,4,6,8,7},按照上面的排序方式,第一趟排序后将8和7交换后,该数组已经有序,接下来的3趟排序就是多余的。因此我们可以增加一个标志,如果某一趟排序没有交换元素,说明这组数据已经有序,不用再继续下去,就可以结束排序。具体的实现代码请看下面:

    /**
	 * 优化一:设置了一个boolean的标志flag,如果这趟排序发生了数据的交换,则flag置为true,否则flag置为false。
	 * @param a
	 */
	public static void bubbleSort1(int[] a) {
		int n = a.length;
		boolean flag = true;  // 第一次判断时,将flag置为true
		for (int i = 1; i < n && flag; i++) {
			flag = false;  // 每趟排序前先将flag置为false
			for (int j = 0; j < n - i; j++) {
				if (a[j] > a[j + 1]) {
					int temp;
					temp = a[j];
					a[j] = a[j + 1];
					a[j + 1] = temp;
					flag = true;  // 表示有数据交换
				}
			}
		}
	}

优化二: 

优化一仅仅适用于连片有序而整体无序的数据(例如:1, 2,3 ,4 ,7,6,5)。但是对于前面大部分是无序而后边小半部分有序的数据(3,2,4,7,4,3,7,8,9,10)排序效率也不可观,对于种类型数据,我们可以继续优化。怎么优化呢?我们可以记下最后一次交换的位置,在最后一次交换的位置后面,必然是有序的,然后下一趟排序从第一个比较到上次记录的位置结束即可,具体的实现代码请看下面:

    /**
	 * 优化二:在优化一的基础上增加了一个位置变量pos,用于记录这趟排序最后发生交换的位置,然后作为下一趟排序比较结束的位置
	 * @param a
	 */
	public static void bubbleSort2(int[] a) {
		int n = a.length;
		boolean flag = true;
		int rightPos = n;  // 最后一次发生交换的位置
		int right = n - 1;
		for (int i = 1; i < n && flag; i++) {
			flag = false;
			for (int j = 0; j < right; j++) {
				if (a[j] > a[j + 1]) {
					int temp;
					temp = a[j];
					a[j] = a[j + 1];
					a[j + 1] = temp;
					flag = true;
					rightPos = j;  // 记录发生交换的位置
				}
			}
			right = rightPos;  // 下一次只需比较到pos的位置即可 
		}
	}

优化三: 

除了上面提到的两种优化方法,还有一种优化方法可以继续提高效率。大致思想是这样:每一趟遍历可以确定两个值,从左到右扫描将最大元素放到最后面,然后从右到左扫描将最小元素放到最前面。例如:待排序列为{1,2,3,4,0},第一趟从做到又扫描得到:{1,2,3,0,4},再一趟从右到左扫描得到{0,1,2,3,4},这样该序列就已经有序了。具体的实现代码请看下面:

    public static void bubbleSort3(int[] a) {
		int n = a.length;
		boolean flag = true;
		int leftPos = 0;  // 左部分最后发生交换的位置
		int left = 0;
		int rightPos = n;  // 右部分最后一次发生交换的位置
		int right = n - 1;
		for (int i = 1; i < n; i++) {
			flag = false;
			// 从左向右扫描将最大值移到最后
			for (int j = 0; j < right; j++) {
				if (a[j] > a[j + 1]) {
					int temp;
					temp = a[j];
					a[j] = a[j + 1];
					a[j + 1] = temp;
					flag = true;
					rightPos = j;  // 记录发生交换的位置
				}
			}
			if(flag) {
				flag = false;
			} else {
				break;
			}
			right = rightPos;
			// 从右到左扫描将最小值移到最前面
			for(int j = right; j > left; j--) {
				if(a[j] < a[j - 1]) {
					int temp = a[j];
					a[j] = a[j - 1];
					a[j - 1] = temp;
					flag = true;
					leftPos = j;
				}
			}
			left = leftPos;
			if(!flag) {
				break;
			}
		}
	}

 算法性能分析:

  • 空间复杂度:

       从空间复杂度上看,冒泡排序仅用了一个辅助单元,空间复杂度为O(1)。 

  • 时间复杂度:

       从时间复杂度上看,最好的情况是序列已经有序时,在第一趟遍历过程中,一次交换都没发生,所以在执行一趟排序之后就结束,这时只需要比较n-1次,不需要交换元素;最坏的情况为逆序状态,总共要进行n-1趟排序,在第i趟排序中,比较次数为n-i次,交换次数为3(n-i)次,则总的比较次数为 (n-1)+(n-2)+(n-3)+...+2+1=n*(n-1)/2次;总的交换次数为3(n-1)+3(n-2)+3(n-3)+...+6+3=3n(n-1)/2次。因此,冒泡排序算法的时间复杂度为O(n²)。

  • 算法稳定性:

       冒泡排序是一种稳定的排序算法。

 

 

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
冒泡排序是一种简单但效率较低的排序算法,它逐个比较相邻的元素并交换位置,直到将最大的元素推到最后。为了优化冒泡排序的效率,可以采取以下几种方法: 1. 设置标志位:在一次完整的冒泡排序过程中,如果没有发生任何元素交换,说明已经排好序了,可以提前结束排序。 2. 减少比较次数:每次冒泡排序都会将最大的元素推到最后,下一轮排序时可以减少比较的次数,因为已经有序的元素不需要再比较。 3. 减少交换次数:可以记录每一轮最后一次交换的位置,下一轮排序时只需要比较到该位置即可。 4. 鸡尾酒排序(双向冒泡排序):正向冒泡将最大元素放到最后,反向冒泡将最小元素放到最前,可以减少排序的回合数。 5. 使用优化冒泡排序算法:如鸟巢排序、魔术师排序等,它们在冒泡排序的基础上进行了一些改进,提高了效率。 下面是一个优化后的冒泡排序Java实现示例: ```java public class BubbleSort { public static void bubbleSort(int[] arr) { int n = arr.length; boolean swapped; for (int i = 0; i < n - 1; i++) { swapped = false; for (int j = 0; j < n - i - 1; j++) { if (arr[j] > arr[j + 1]) { // 交换元素 int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; swapped = true; } } // 如果没有发生交换,则说明已经排好序 if (!swapped) { break; } } } public static void main(String[] args) { int[] arr = {64, 34, 25, 12, 22, 11, 90}; bubbleSort(arr); System.out.println("排序后的数组:"); for (int num : arr) { System.out.print(num + " "); } } } ``` 这是一个基本的冒泡排序算法,通过设置标志位来判断是否已经排好序,如果没有发生交换,则提前结束排序。你可以根据实际需求进行进一步的优化

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值