⭐️排序算法⭐️
📚最快最简单的排序算法——桶排序
🔐举个栗子:
期末考试完了老师要将同学们的分数按照从高到低排序。小哼的班上只有5个同学,这5个同学分别考了5分、3分、5分、2分和8分,哎考得真是惨不忍睹(满分是10分)。接下来将分数进行从大到小排序,排序后是85532。你有没有什么好方法编写一段程序,让计算机随机读入5个数然后将这5个数从大到小输出?
💡分析
首先我们需要申请一个大小为11的数组int a[11]。OK,现在你已经有了11个变量,编号从a[0] ~ a[10]。刚开始的时候,我们将a[0] ~ a[10]都初始化为0,表示这些分数还都没有人得过。例如 a[0]等于0就表示目前还没有人得过0分,同理a[1]等于0就表示目前还没有人得过1分……a[10]等于0就表示目前还没有人得过10分。
下面开始处理每一个人的分数,第一个人的分数是5分,我们就将相对应的a[5]的值在原来的基础增加1,即将a[5]的值从0改为1,表示5分出现过了一次。
同理第二个人的分数为3,我们就把相对应的 a[3]的值在原来的基础上增加1,即将a[3]的值从0改为1,表示3分出现过了一次。
注意此时第三个人的分数也是5分,所以 a[5]的值需要在此基础上再增加1,即将a[5]的值从1改为2,表示5分出现过了两次。
按照刚才的方法处理第四个和第五个人的分数。最终结果就是下面这个图啦。
你发现没有,a[0]~a[10]中的数值其实就是0分到10分每个分数出现的次数。接下来,我们只需要将出现过的分数打印出来就可以了,出现几次就打印几次,具体如下。
最终屏幕输出“23558”。
🔑Java代码
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int a[] =new int[11];
int k;
for(int i=0;i<=10;i++){
a[i]=0; //初始化为0
}
for(int i=0;i<5;i++){ //读入五个数
k=sc.nextInt(); //把每一个数读到变量k中
a[k]++; //进行计数
}
for(int i=10;i>=0;i--){ //依次判断a[10]-a[0](题目要求从大到小输出)
for(int j=i;a[j]>0;a[j]--){ //出现几次就打印几次
System.out.print(i+" ");
}
}
}
}
输入:
5 3 5 2 8
输出:
8 5 5 3 2
📒时间复杂度
该算法的时间复杂度是O(m+n+m+n)即O(2*(m+n))。我们在说时间复杂度的时候可以忽略较小的常数,最终桶排序的时间复杂度为O(m+n)。还有一点,在表示时间复杂度的时候,n和m通常用大写字母即 O(M+N)。
📒说明
📒问题的引出:
📚冒泡排序
💡基本思想
冒泡排序的基本思想是:每次比较两个相邻的元素,如果它们的顺序错误就把它们交换过来。
💡思想分析
🔐举个栗子:
将12 35 99 18 76这5个数进行从大到小的排序。
🌱第一趟:
首先比较第1位和第2位的大小,现在第1位是12,第2位是35。发现12比35要小,因为我们希望越小越靠后嘛,因此需要交换这两个数的位置。交换之后这5个数的顺序是:
按照刚才的方法,继续比较第2位和第3位的大小,第2位是12,第3位是99。12比99要小,因此需要交换这两个数的位置。交换之后这5个数的顺序是:
根据刚才的规则,继续比较第3位和第4位的大小,如果第3位比第4位小,则交换位置。交换之后这5个数的顺序是:
最后,比较第4位和第5位。4次比较之后5个数的顺序是:
经过4次比较后我们发现最小的一个数已经就位(已经在最后一位,请注意12这个数的移动过程),是不是很神奇。现在再来回忆一下刚才比较的过程。每次都是比较相邻的两个数,如果后面的数比前面的数大,则交换这两个数的位置。一直比较下去直到最后两个数比较完毕后,最小的数就在最后一个了。就如同是一个气泡,一步一步往后“翻滚”,直到最后一位。所以这个排序的方法有一个很好听的名字“冒泡排序”。
下面我们将继续重复刚才的过程,将剩下的4个数一一归位。
🌱第二趟:
现在开始“第二趟”,目标是将第2小的数归位。首先还是先比较第1位和第2位,如果第1位比第2位小,则交换位置。交换之后这5个数的顺序是:
接下来你应该都会了,依次比较第⒉位和第3位,第3位和第4位。注意此时已经不需要再比较第4位和第5位。因为在第一趟结束后已经可以确定第5位上放的是最小的了。第二趟结束之后这5个数的顺序是:
🌱第三趟:
第三趟之后这5个数的顺序是:
🌱第四趟:
现在到了最后一趟“第四趟”。有的同学又要问了,这不是已经排好了吗?还要继续?当然,这里纯属巧合,你若用别的数试一试可能就不是了。
💡冒泡排序的原理
⭐️总结
如果有 n 个数进行排序,只需将n-1个数归位,也就是说要进行n-1趟操作。而“每一趟”都需要从第 1 位开始进行相邻两个数的比较,将较小的一个数放在后面,比较完毕后向后挪一位继续比较下面两个相邻数的大小,重复此步骤,直到最后一个尚未归位的数,已经归位的数则无需再进行比较(已经归位的数你还比较个啥?,浪费表情)。
🔑Java代码
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int[] a=new int[100];
int n=sc.nextInt(); //输入一个数n,表示接下来有n个数
int t;
for(int i=1;i<=n;i++){ //循环读入n个数到数组a中
a[i]=sc.nextInt();
}
//冒泡排序的核心部分
for(int i=1;i<=n-1;i++){ //n个数排序,只用进行n-1趟
for(int j=1;j<=n-i;j++){ //从第1位开始比较直到最后一个尚未归位的数,
// 想一想为什么到n-i就可以了。
if(a[j]<a[j+1]){ //比较大小并交换
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
}
for(int i=1;i<=n;i++){ //输出结果
System.out.print(a[i]+" ");
}
}
}
运行结果:
输入:
10
1 2 3 4 5 6 7 8 9 10
输出:
10 9 8 7 6 5 4 3 2 1
现在我们就可以来解决之前桶排序留下的问题了
🔑代码如下:
import java.util.Scanner;
class Student{
String name;
int score;
}
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
Student[] s=new Student[100];
for (int i = 0; i < 100; i++){
s[i]=new Student(); //注意这一步,初始化每个数组
}
Student s2;
int n=sc.nextInt();
for(int i=0;i<n;i++){
s[i].name=sc.next();
s[i].score=sc.nextInt();
}
for(int i=0;i<n-1;i++){
for(int j=0;j<n-i;j++){
if(s[j].score<s[j+1].score){
s2=s[j];
s[j]=s[j+1];
s[j+1]=s2;
}
}
}
for(int i=0;i<n;i++){
System.out.println(s[i].name);
}
}
}
运行结果:
输入:
5
huhu 5
haha 3
xixi 5
hengheng 2
gaoshou 8
输出:
gaoshou
huhu
xixi
haha
hengheng
📒时间复杂度
冒泡排序的核心部分是双重嵌套循环。不难看出冒泡排序的时间复杂度是 O(N*N)。这是一个非常高的时间复杂度。