背景
实际应用示例
-
电子表格软件(如Excel) :
- 选择要排序的数据区域。
- 打开“自定义排序”或“排序”对话框。
- 在“排序依据”下拉列表中,选择第一个需要排序的列(例如,“班级”)。
- 在“次序”下拉列表中选择排序方式(例如,“升序”)。
- 点击“添加”按钮,添加第二个排序依据(例如,“姓名”),并设置排序方式。
- 重复此步骤,直到所有需要排序的列都添加完毕,然后单击“确定”。
-
SQL 数据库:
- 在SQL 语句中使用
ORDER BY子句,并列出多个字段来指定多重排序的顺序。 - 例如,
ORDER BY 班级ASC, 姓名DESC表示首先按“班级”升序排序,然后在班级相同的情况下,再按“姓名”降序排序。
- 在SQL 语句中使用
-
文献排序:
- 在参考文献列表中,通常会先按作者姓氏拼音排序,如果作者姓氏相同,则按作者名字拼音排序;如果作者姓名都相同,则按出版年份排序,依此类推
实现原理
在Java 中对二维数组进行分组排序,可以使用 Arrays.sort() 方法结合自定义 Comparator 或Lambda 表达式来实现,具体取决于分组依据。 你可以定义一个比较器,指定比较两个子数组的规则,例如先按第一列排序,如果第一列相同,则按第二列排序。
使用 Arrays.sort() 和 Comparator
这种方法通过实现一个自定义比较器来控制排序逻辑
import java.util.Arrays;
import java.util.Comparator;
public class Sort2DArray {
public static void main(String[] args) {
int[][] arr = {
{1, 5},
{3, 2},
{1, 3},
{2, 8}
};
// 按照第一列升序,第一列相同则按第二列升序排序
Arrays.sort(arr, new Comparator<int[]>() {
@Override
public int compare(int[] a, int[] b) {
// 比较第一列
if (a[0] != b[0]) {
return Integer.compare(a[0], b[0]);
} else {
// 如果第一列相同,比较第二列
return Integer.compare(a[1], b[1]);
}
}
});
// 打印排序后的数组
for (int[] row : arr) {
System.out.println(Arrays.toString(row));
}
}
}
使用 Arrays.sort() 和Lambda 表达式
对于Java 8 及以上版本,可以使用Lambda 表达式简化 Comparator 的实现,使代码更简洁
import java.util.Arrays;
public class Sort2DArrayLambda {
public static void main(String[] args) {
int[][] arr = {
{1, 5},
{3, 2},
{1, 3},
{2, 8}
};
// 使用 Lambda 表达式实现自定义比较器
Arrays.sort(arr, (a, b) -> {
if (a[0] != b[0]) {
return Integer.compare(a[0], b[0]);
} else {
return Integer.compare(a[1], b[1]);
}
});
// 打印排序后的数组
for (int[] row : arr) {
System.out.println(Arrays.toString(row));
}
}
}
自定义调整: 我可以看到,仅需调整a和b的比较顺序,就可以轻松实现多组排序的升序/降序,先比较数据,后比较数据 首先根据第一个关键字段进行排序,当第一个字段的值相同时,再根据第二个关键字段进行排序,以此类推。 这种方法常用于对包含多个信息列的数据进行排序,以实现更精细化的数据组织
层层递进: 如果第二层排序结果中仍有相同的值,则会继续按照你指定的第三个、第四个(依此类推)字段进行排序,直到所有排序键都比较完毕
应用场景
生活中很多排序也会使用多组排序的思路,例如,学生成绩总分排序,成绩一致情况,按照学号排序 算法题目中涉及多个属性问题,可能会使用到,如下,我们举例一个算法题目帮助大家理解
leetcode 354. 俄罗斯套娃信封问题 在这道题目中,我们对于信封的两个数据 宽度和高度 进行分组排序
import java.util.*;
class Solution {
public int maxEnvelopes(int[][] envelopes) {
if (envelopes == null || envelopes.length == 0) {
return 0;
}
// 使用Lambda表达式简化排序
Arrays.sort(envelopes, (a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]);
List<Integer> g = new ArrayList<>();
for (int[] envelope : envelopes) {
int h = envelope[1];
// 使用Collections.binarySearch
int index = Collections.binarySearch(g, h);
if (index < 0) {
index = -(index + 1);
}
if (index == g.size()) {
g.add(h);
} else {
g.set(index, h);
}
}
return g.size();
}
}
算法思路解释:
-
排序策略:先按宽度升序,宽度相同时按高度降序
- 这样确保在相同宽度的信封中,我们只会选择其中一个(因为高度是降序的)
- 将二维问题转化为一维的高度序列的最长递增子序列问题
-
最长递增子序列(LIS) :
- 使用贪心 + 二分查找的方法
- 维护一个列表
g,其中g[i]表示长度为i+1的递增子序列的最小末尾值 - 对于每个高度,找到它在
g中的插入位置并更新
时间复杂度:O(n log n)
空间复杂度:O(n)
这种方法巧妙地利用排序将二维问题降为一维,然后使用经典的LIS算法求解