排序算法(一):冒泡排序

一、BubbleSort冒泡排序

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

算法描述

  1. 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
  3. 针对所有的元素重复以上的步骤,除了最后一个;
  4. 重复步骤1~3,直到排序完成。

二、案例

给定一个数组:[3,5,1,7,8,9]

  1. 以下是进行第一趟排序的过程
    排序算法(一):冒泡排序_第1张图片

经过以上的排序得到【3,1,5,7,8,9】,这样最后一个元素就是这一趟排序的最大值了,进行第二趟排序时待排的元素就是[3,1,5,7,8]。
依次类推:
第二趟的结果:【1,3,5,7,8,9】 待排元素【1,3,5,7】
第三趟的结果:【1,3,5,7,8,9】 待排元素【1,3,5】
第四趟的结果:【1,3,5,7,8,9】 待排元素【1,3】
第五趟的结果:【1,3,5,7,8,9】 待排元素【1】
待排元素的长度为1了,不需要进行下一趟排序了

三、代码实现

//冒泡排序未优化版,时间复杂度为O(n^2)
	public static void bubbleSort(int[] nums) {
     
		int i;
		//排序的趟数
		for(i=0;i<nums.length-1;i++) {
     
			//比较的次数
			for(int j=0;j<nums.length-1-i;j++) {
      
				//比较相邻的,大的替换
				if(nums[j]>nums[j+1]) {
     
					int temp=nums[j];
					nums[j]=nums[j+1];
					nums[j+1]=temp;
				}
			}
			System.out.println("第"+i+"趟排序的结果:"+Arrays.toString(nums));
		}
	}

测试类及其结果

public static void main(String[] args) {
     
	int[] nums=new int[] {
     3,1,5,7,8,9};
	bubbleSort(nums);
}

运行结果如下:
排序算法(一):冒泡排序_第2张图片

四、时间和空间复杂度分析

1、稳定性分析:

稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b的后面。

冒泡排序中通过比较大小交换位置,相等的情况下不会交换,所以是稳定的。

2、时间复杂度
最差情况下:总共进行n-1趟排序,每一趟都要比较n-i次,时间复杂度为O(n^2)
最佳情况:T(n) = O(n) 最差情况:T(n) = O(n^2) 平均情况:T(n) = O(n ^2)
3、空间复杂度
不需要借助额外的空间,所以是O(1);

五、优化

优化一适用于连片有序而整体无序的数据

上面的例子排序是【3,1,5,7,8,9】,在第二趟排序中是不需要进行任何交换的,所以第三趟排序以及之后是不需要进行的,只需要两趟就能确定已经排好。

思路:加个标志位记录本趟排序是否发生交换

//冒泡排序优化版本
//如果这一趟排序没有进行任何交换,说明已经排好了即可跳出
public static void bubbleSort_cp(int[] nums) {
     
	int temp,flag;
	int i;
	for(i=0;i<nums.length-1;i++) {
     
		//每一趟排序标志位都先置为0
		flag=0;
		for(int j=0;j<nums.length-1-i;j++) {
     
			if(nums[j]>nums[j+1]) {
     
				temp=nums[j];
				nums[j]=nums[j+1];
				nums[j+1]=temp;
				//发生交换时标志位置为1
				flag=1;
			}
		}
		System.out.println("第"+i+"趟排序的结果:"+Arrays.toString(nums));
		if(flag==0) 
			break;
	}
}

优化过后,如果数组已经有序则时间复杂度为O(n);
排序算法(一):冒泡排序_第3张图片
由结果可以看出只进行了2趟排序

优化二: 大致思想就是一次排序可以确定两个值,正向扫描找到最大值交换到最后,反向扫描找到最小值交换到最前面。

例如:排序数据1,2,3,4,5,6,0

//优化二:鸡尾排序
//奇数趟:从左到右,偶数趟从右向左
public static void bubbleSort_cp_1(int[] nums) {
     
	for(int i=0;i<nums.length-1;i++) {
     
		int flag=0;
		//正向排序,从左往右进行比较
		for(int j=0;j<nums.length-1-i;j++) {
     
			if(nums[j]>nums[j+1]) {
     
				int temp=nums[j+1];
				nums[j+1]=nums[j];
				nums[j]=temp;
				flag=1;
			}
		}
		if(flag==0)
			break;
		//逆向排序从右往左进行比较
		for(int k=nums.length-2-i;k>0;k--) {
     
			if(nums[k-1]>nums[k]) {
     
				int temp = nums[k-1];
				nums[k-1]=nums[k];
				nums[k]=temp;
				flag=1;
			}
		}
		if(flag==0)
			break;
	}
}

采用优化一进行的趟数
排序算法(一):冒泡排序_第4张图片
采用优化二进行的趟数
在这里插入图片描述
这是由于双向循环,能够将最大值和最小值交换到对应的位置,减少了排序的趟数

参考文章:
https://blog.csdn.net/hansionz/article/details/80822494
https://blog.csdn.net/weixin_43232955/article/details/102761799
https://www.cnblogs.com/guoyaohua/p/8600214.html

你可能感兴趣的:(数据结构和算法)