《啊哈!算法》第一章 - 第二节 - 冒泡排序(Java实现)

《啊哈!算法》第一章 - 第二节 - 冒泡排序(Java实现)

冒泡排序

冒泡排序的基本思想:每次比较两个相邻的元素,如果它们的顺序错误就把它们交换过来
举个例子:

将 12 35 99 18 76 这 5 个数进行从大到小的排序

既然是从小到大排序,那肯定就是越小越靠后了。
首先比较第 1 位和第 2 位数字的大小,现在第 1 位是 12,第 2 位是 35。发现 12 比 35 要小,因为我们希望越小越靠后嘛,因此需要交换这两个数字的位置。交换之后这 5 个数字的顺序是 35 12 99 18 76。同理继续比较第 2 位和第 3 位数字的大小……直到第 4 位和第 5 位比较结束后, 5个数的顺序是 35 99 18 76 12。
经过 4 次比较后我们发现小的一个数已经就位(已经在后一位,请注意 12 这个数 的移动过程),是不是很神奇?我们现在再来回忆一下刚才比较的过程。每次都是比较相邻的两个数,如果后面的数比前面的数大,则交换这两个数的位置。一直比较下去直到后两个数比较完毕后,小的数就在最后一个了,就如同是一个气泡,一步一步往后“翻滚”,直到最后一位。所以这个排序的方法有一个很好听的名字 —— 冒泡排序,如下图所示:
在这里插入图片描述
但其实到现在我们的排序只将 5 个数中最小的一个数字归位了。每将一个数字归位我们将其称为“一趟”。之后我们继续重复刚才的过程,将剩下的 4个数一一归位。
“冒泡排序”的原理是:每一趟只能确定将一个数字归位。即第一趟只能确定将末位上的 数(即第 5 位)归位,第二趟只能将倒数第 2 位上的数(即第 4位)归位,第三趟只能将倒数第 3 位上的数(即第 3位)归位……直到将每一个数都归位。

小总结
如果有 n 个数进行排序,只需将 n - 1 个数归位,也就是说要进行 n -1 趟操作。 而 “ 每一趟 ” 都需要从第 1 位开始进行相邻两个数字的比较,将较小的一个数字放在后面,比较完毕后向后挪一位继续比较下面两个相邻数字的大小,重复此步骤,直到最后一个尚未归位的数字,已经归位的数字则无需再进行比较。

了解完冒泡排序是什么,我们就开始实操吧!

冒泡排序的升序排序

题目同上:

期末考试完了老师要将同学们的分数按照从高到低排序。小哼的班上只有 5 个同学,这 5 个同学分别考了 5分、3分、 5分、2分和8分,考得真是惨不忍睹(满分是 10 分)。接下来将分数进行从大到小排序, 排序后是 8 5 5 3 2。你有没有什么好方法编写一段程序,让计算机随机读入 5 个数然后将这 5个数从大到小输出?请先想一想,至少想 5 分钟再往下看吧(__) 。

思路解析:
题目可以用下面的一句话进行概括:

给你 5 个 0~10 的乱序数字,将这 5 个数字按照从大到小的顺序进行输出

明确题目之后,我们该怎么做呢?

  1. 创建一个长度为 5 的数组,将需要排序的数字存到数组里
	int a[] = new int[5]; // 创建长度为5的数组
	Scanner sc = new Scanner(System.in); // 输入需要排序的数字
	for(int i = 0; i < a.length; i++){ //  循环读入需要排序的数字
		a[i] = sc.nextInt();
	}
  1. 根据冒泡排序原理开始对数字进行排序
//	    冒泡排序核心代码
//	    如果有n个数,外层循环只需要循环n-1次,可以理解为n个数之间的间隔数
		for(int i = 0; i < a.length-1; i++) {
//			j 是 i 的邻居,是第二个数
			for(int j = i+1; j < a.length-i; j++) {
//				两个数比大小,位置错了就换回来
				if(a[i] > a[j]) {
//					交换值的方式一:中间变量法
//					int t = a[j];
//					a[j] = a[i];
//					a[i] = t;
					
//					交换值的方式二:异或运算交换两个数的值
					a[i] = a[i]^a[j];
					a[j] = a[j]^a[i];
					a[i] = a[i]^a[j];
					
//					交换值的方式三:两值之和
//					int t = a[i] + a[j];
//					a[i] = t - a[i];
//					a[j] = t - a[j];
				}
			}
		}
  1. 遍历输出数组
//		遍历输出数组方法一:直接输出数字
		for(int i = 0; i < a.length; i++) {
			System.out.print(a[i]+" ");
		}

//		遍历输出数组方法二:以数组的形式输出
		System.out.print(Arrays.toString(a));

完整代码如下:

import java.util.Arrays;
import java.util.Scanner;

public class T2 {

	public static void main(String[] args) {
		// 冒泡排序
		
//		输入需要排序的数字个数
		System.out.println("请输入需要排序的数字个数:");
		Scanner s1 = new Scanner(System.in);
		int n = s1.nextInt();
		int a[] = new int[n];
//		输入需要排序的数字
		System.out.println("请输入需要排序的数字:");
		Scanner s2 = new Scanner(System.in);
		for(int i = 0; i < a.length; i++) {
//			循环把输入的数读到数组中
			a[i] = s2.nextInt();
		}
		
//		冒泡排序核心代码
//	        如果有n个数,外层循环只需要循环n-1次,可以理解为n个数之间的间隔数
		for(int i = 0; i < a.length; i++) {
//			j 是 i 的邻居,是第二个数
			for(int j = i+1; j < a.length; j++) {
//				两个数比大小,位置错了就换回来
				if(a[i] > a[j]) {
//					交换值的方式一:中间变量,双斜线
//					int t = a[j];
//					a[j] = a[i];
//					a[i] = t;
					
//					交换值的方式二:异或运算交换两个数的值
					a[i] = a[i]^a[j];
					a[j] = a[j]^a[i];
					a[i] = a[i]^a[j];
					
//					交换值的方式三:两值之和
//					int t = a[i] + a[j];
//					a[i] = t - a[i];
//					a[j] = t - a[j];
				}
			}
		}
//		遍历输出数组方法一
//		for(int i = 0; i < a.length; i++) {
//			System.out.print(a[i]+" ");
//		}
		
//		遍历输出数组方法二
		System.out.print(Arrays.toString(a));
	}
}

输入: 5 3 5 2 8
第一种输出方式: 2 3 5 5 8
第二种输出方式: [2,3,3,5,8]
运行结果如图所示:
在这里插入图片描述

冒泡排序的降序排序

那么利用冒泡排序进行升序排序写完了,但是题目要求的是将 5 个数字按照从大到小的顺序输出,这该怎么办呢?别急,我们只需要改动一个条件即可。
如果你仔细观察了可以发现,冒泡排序中决定输出顺序的关键就在于冒泡排序核心代码的双重循环里的 if 条件判断,我们现在的 if 条件判断是 a[i] > a[j],即如果 a[i] > a[j],前一个数字大于后一个数字的话,就将这两个数字调换顺序,顺序调换之后,小数在前,大数在后,即升序排序。但是我们需要的是降序排序,因此只需要改动一下 if 条件判断即可。

改动代码如下:

//		升序
	for(int i = 0; i < a.length-1; i++) {
//			j 是 i 的邻居,是第二个数
			for(int j = i+1; j < a.length-i; j++) {
//				两个数比大小,位置错了就换回来
				if(a[i] > a[j]) {  
				//升序是判断第一个数是否大于第二个数,如果大于,就把第一个数和第二个数对调位置
//					交换值的方式二:异或运算交换两个数的值
					a[i] = a[i]^a[j];
					a[j] = a[j]^a[i];
					a[i] = a[i]^a[j];
				}
			}

//		降序
	for(int i = 0; i < a.length-1; i++) {
//			j 是 i 的邻居,是第二个数
			for(int j = i+1; j < a.length-i; j++) {
//				两个数比大小,位置错了就换回来
				if(a[i] < a[j]) {  
				//降序是判断第一个数是否小于第二个数,如果小于,就把第一个数和第二个数对调位置
//					交换值的方式二:异或运算交换两个数的值
					a[i] = a[i]^a[j];
					a[j] = a[j]^a[i];
					a[i] = a[i]^a[j];
				}
			}

完整代码如下:

import java.util.Arrays;
import java.util.Scanner;

public class T2 {

	public static void main(String[] args) {
		// 冒泡排序
		
//		输入需要排序的数字个数
		System.out.println("请输入需要排序的数字个数:");
		Scanner s1 = new Scanner(System.in);
		int n = s1.nextInt();
		int a[] = new int[n];
//		输入需要排序的数字
		System.out.println("请输入需要排序的数字:");
		Scanner s2 = new Scanner(System.in);
		for(int i = 0; i < a.length; i++) {
//			循环把输入的数读到数组中
			a[i] = s2.nextInt();
		}
		
//		冒泡排序核心代码
//	        如果有n个数,外层循环只需要循环n-1次,可以理解为n个数之间的间隔数
		for(int i = 0; i < a.length; i++) {
//			j 是 i 的邻居,是第二个数
			for(int j = i+1; j < a.length; j++) {
//				两个数比大小,位置错了就换回来
				if(a[i] > a[j]) {
//					交换值的方式一:中间变量,双斜线
//					int t = a[j];
//					a[j] = a[i];
//					a[i] = t;
					
//					交换值的方式二:异或运算交换两个数的值
					a[i] = a[i]^a[j];
					a[j] = a[j]^a[i];
					a[i] = a[i]^a[j];
					
//					交换值的方式三:两值之和
//					int t = a[i] + a[j];
//					a[i] = t - a[i];
//					a[j] = t - a[j];
				}
			}
		}
//		遍历输出数组方法一
//		for(int i = 0; i < a.length; i++) {
//			System.out.print(a[i]+" ");
//		}
		
//		遍历输出数组方法二
		System.out.print(Arrays.toString(a));
	}
}

输入: 5 3 5 2 8
第一种输出方式: 8 5 5 3 2
第二种输出方式: [8,5,5,3,2]
运行结果如图所示:
在这里插入图片描述

时间复杂度
最后来说下时间复杂度的问题。
冒泡排序的时间复杂度是 O(N2),这是一个非常高的时间复杂度。
注意
冒泡排序解决了桶排序浪费空间的问题,但在算法的执行效率上却牺牲了很多,它的时间复杂度达到了 O(N2)。 假如我 们的计算机每秒钟可以运行 10 亿次,那么对 1 亿个数进行排序,桶排序只需要 0.1 秒,而冒泡排序则需要 1 千万秒,达到 115 天之久。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爪喵喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值