蓝桥杯几乎每年都会有让我们先进行排序才能解决的题目,不知道屏幕前的小伙伴是否还在手写冒泡排序呢?其实很多语言都自带了排序函数sort(),我们只需要会调用就可以了。
当然,看到这里小伙伴们也不要以为知道了sort()函数的存在就万事大吉了,直接让我们对数组从小到大排序这么简单的情况其实很少出现。在Java中,一般要排序的内容往往是一个对象,而且题目中的排序规则会复杂一些,需要我们自定义排序方法的比较器。
☕☕尤其是使用Java的同学,java.util包中为我们提供了很强大的排序工具类。学会使用Java中的排序API,掌握如何重写比较器,不仅仅对我们参加比赛很有帮助,熟练掌握Java API也是一名Java开发者应该具备的基本能力,接下来就让我们一起看看Java中的sort()方法吧~
题目传送门:(题目来源与蓝桥杯练习题库-算法提高)
| 题目 | 链接 |
|---|---|
| 成绩排名 | http://lx.lanqiao.cn/problem.page?gpid=T551 |
| 成绩排序 | http://lx.lanqiao.cn/problem.page?gpid=T557 |
排序API详解:
在java.util包中,Collections类与Arrays类都提供了sort()方法,除了基本的从小到大排序外,它们都支持自定义排序规则以满足我们的实际需求。两者的区别在于Arrays中的sort()方法用于对数组排序,Collections中的sort()方法用于对list列表排序。
我们先来了解一下sort函数的基本用法,至于如何自定义比较规则等下再说~
不管是Collections.sort(),还是Arrays.sort(),除了作用对象不同,其他的都是一样的,这里就放在一起说了。
升序排序:
sort(待排数组或者列表)
区间排序(区间左闭右开):
sort(待排数组或者列表, 起始下标, 终止下标)
自定义排序:
sort(待排, 比较器)
这里自定义排序规则是指对某个对象的数组或者列表进行自定义排序,对于基本类型的排序比如int型数组想要逆序排序是不能使用这种方法的,必须使用它的包装类Integer数组,才能进行自定义排序。
比如可以这样写(比较器怎么写后面有讲~):
Integer[] nums = new Integer[]{4, 2, 6, 10, 3};
Arrays.sort(nums, new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
如果仅仅对int型数组逆序排序,可以先顺序排序后倒序访问,这样更简单。
下面我们来具体看一下如何自定义比较规则。
自定义比较规则:首先定义一个person对象,将person对象添加至数组中:
class person{
String name;
int age;
public person(String name, int age) {
this.name = name;
this.age = age;
}
}
将年龄不同的三人添加至List。
public static void main(String[] args) {
ArrayList people = new ArrayList<>();
people.add(new Person("小明", 12));
people.add(new Person("小王", 30));
people.add(new Person("小红", 10));
}
如果我们需要对三个人按照年龄排序,Java提供了两种方法来自定义比较规则~
1.继承Comparable接口:我们可以让Person类继承Comparable接口,并实现其中的compareTo()方法,来自定义排序规则。
class Person implements Comparable{ String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public int compareTo(Person o) { return this.age - o.age; } }
return this.age - o.age代表我们是以年龄按照升序排序,如果按照降序排序,应该写成return o.age - this.age。
⭐可以简单理解为,如果当前的值大于参数中的值,当前值减参数值,相减结果为正,那么在排序时就要交换这两个值,把大的数放后面,也就是正序排序;同理,如果参数的值大于当前值,参数值减当前值,相减结果为正,那么在排序时就要交换这两个值,把大的数放前面,也就是逆序排序。(有点拗口,建议多读两遍~)
需要排序时,直接调用sort()即可(排序List使用Collections下的sort)。
Collections.sort(people);
结果:
[Person{name='小红', age=10}, Person{name='小明', age=12}, Person{name='小王', age=30}]
2.实现Comparator接口中的compare方法:
上一种方法我们是将类继承了Comparable接口,比较规则也只能有一个,如果我们需要根据不同情况使用不同的比较规则,可以使用第二种方法。
在sort(待排, 比较器)这个方法中,第二个参数可以传入一个比较器Comparator,所以我们可以使用匿名内部类的方式,传入Comparator类并重写其中的compare方法。
比如下面这样:
Collections.sort(people, new Comparator() { @Override public int compare(Person o1, Person o2) { return o1.age - o2.age; } });
return o1.age - o2.age代表按照年龄升序排序,如果反过来写:return o2.age - o1.age代表按照年龄逆序排序。
⭐可以简单理解为,两个参数,如果前大后小返回真,代表正序排序;前小后大返回真,代表逆序排序。
实战演练:
看完了如何自定义比较规则让对象也具备排序的能力以后,让我们看看如何再题目中使用吧。
下面两道蓝桥杯的模拟题,排序规则会复杂一些,需要对多个数据项定义比较规则。
大家在浏览代码时请留意:
第一道题使用Arrays.sort() + 实现Comparator接口进行排序,第二道题使用Collections.sort() + 继承Comparable接口进行排序,两种方式都带大家体验一下。
会了下面两道题,Java的排序API就可以说是融会贯通了。✨✨
蓝桥杯练习题库-算法提高: 1.成绩排名需要注意的地方:资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
小明刚经过了一次数学考试,老师由于忙碌忘记排名了,于是老师把这个光荣的任务交给了小明,小明则找到了聪明的你,希望你能帮他解决这个问题。
输入格式
第一行包含一个正整数N,表示有个人参加了考试。接下来N行,每行有一个字符串和一个正整数,分别表示人名和对应的成绩,用一个空格分隔。
输出格式
输出一共有N行,每行一个字符串,第i行的字符串表示成绩从高到低排在第i位的人的名字,若分数一样则按人名的字典序顺序从小到大。
样例输入
3 aaa 47 bbb 90 ccc 70样例输出
bbb ccc aaa 【数据规模和约定】 人数<=100,分数<=100,人名仅包含小写字母。
题目要求如果分数相同,则比较名字的字典序,属于多个数据项的比较规则,在比较器中我们可以这样写:
public int compare(Student o1, Student o2) {
if (o1.score == o2.score)
return o1.name.compareTo(o2.name);
else
return o2.score - o1.score;
}
先判断分数是否相同,如果相同则比较名字的字典序,不同再比较分数。
比较字符串的字典序不能直接使用“<”或者“>”,应该使用compareTo方法:
o1.name.compareTo(o2.name)表示o1.name的字典序比o2.name的字典序大时返回真。
AC代码(Java):import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
class Student {
String name;
int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Student[] students = new Student[n];
for (int i = 0; i < n; i++) {
// 使用对象数组要先对每一个对象进行实例化
String name = sc.next();
int score = sc.nextInt();
Student student = new Student(name, score);
students[i] = student;
}
Arrays.sort(students, new Comparator() {
@Override
public int compare(Student o1, Student o2) {
if (o1.score == o2.score)
return o1.name.compareTo(o2.name);
else
return o2.score - o1.score;
}
});
for (int i = 0; i < students.length; i++)
System.out.println(students[i].name);
}
}
2.成绩排序
需要注意的地方:资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
给出n个学生的成绩,将这些学生按成绩排序,
排序规则,优先考虑数学成绩,高的在前;数学相同,英语高的在前;数学英语都相同,语文高的在前;三门都相同,学号小的在前输入格式
第一行一个正整数n,表示学生人数
接下来n行每行3个0~100的整数,第i行表示学号为i的学生的数学、英语、语文成绩输出格式
输出n行,每行表示一个学生的数学成绩、英语成绩、语文成绩、学号
按排序后的顺序输出样例输入
2 1 2 3 2 3 4样例输出
2 3 4 2 1 2 3 1 数据规模和约定 n≤10
这题的排序规则更为复杂,不过所谓换汤不换药,参照上一题,比较规则还是很容易写的。我们还是考虑三科成绩都相等时,按学号升序;数学英语都相同时,按语文降序…
所以可以写成下面这样:
public int compareTo(Student o) {
if (this.mathScore == o.mathScore && this.englishScore == o.englishScore && this.chineseScore == o.chineseScore)
return this.id - o.id;
else if (this.mathScore == o.mathScore && this.englishScore == o.englishScore)
return o.chineseScore - this.chineseScore;
else if (this.mathScore == o.mathScore)
return o.englishScore - this.englishScore;
else
return o.mathScore - this.mathScore;
}
本题通过让类继承Comparable接口,重写里面的compareTo方法来完成自定义排序,注意方法中参数只有一个,待比较的两个数是当前对象的属性值和参数中对象的属性值。
至于是正序还是逆序,可以将this当作上一题中compare()方法里的o1,将o当作上一题中compare()方法里的o2,返回值 o1 - o2 为正序,o2 - o1 为逆序。
AC代码(Java):import java.util.ArrayList; import java.util.Collections; import java.util.Scanner; class Student implements Comparable{ int id; int mathScore; int englishScore; int chineseScore; public Student(int id, int mathScore, int englishScore, int chineseScore) { this.id = id; this.mathScore = mathScore; this.englishScore = englishScore; this.chineseScore = chineseScore; } @Override public String toString() { return this.mathScore + " " + this.englishScore + " " + this.chineseScore + " " + this.id; } @Override public int compareTo(Student o) { if (this.mathScore == o.mathScore && this.englishScore == o.englishScore && this.chineseScore == o.chineseScore) return this.id - o.id; else if (this.mathScore == o.mathScore && this.englishScore == o.englishScore) return o.chineseScore - this.chineseScore; else if (this.mathScore == o.mathScore) return o.englishScore - this.englishScore; else return o.mathScore - this.mathScore; } } public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); ArrayList students = new ArrayList<>(); for (int i = 0; i < n; i++) { int id = i + 1; int mathScore = sc.nextInt(); int englishScore = sc.nextInt(); int chineseScore = sc.nextInt(); Student student = new Student(id, mathScore, englishScore, chineseScore); students.add(student); } Collections.sort(students); for (Student student : students) { System.out.println(student); } } }
单纯调用sort()方法还是很简单的,本文唯一的难点就是自定义比较规则,需要我们继承Comparable接口并重写compareTo方法或者在sort方法中传入匿名内部类Comparator比较器并重写compare方法,同时多个比较条件如何编写代码也是本文的重点和难点,希望同学们多加练习,亲手实践~
创作不易,如果觉得本文对你有所帮助的话请动动小手,给博主点个免费的赞吧。♀️
@作者:Mymel_晗,计科专业大学牲菜狗一枚,请大佬们多多关照~



