【Leetcode 每日一题】2545. 根据第 K 场考试的分数排序

embedded/2024/12/26 0:13:33/

问题背景

班里有 m m m 位学生,共计划组织 n n n 场考试。给你一个下标从 0 0 0 开始、大小为 m × n m \times n m×n 的整数矩阵 s c o r e score score,其中每一行对应一位学生,而 s c o r e [ i ] [ j ] score[i][j] score[i][j] 表示第 i i i 位学生在第 j j j 场考试取得的分数。矩阵 s c o r e score score 包含的整数 互不相同
另给你一个整数 k k k。请你按第 k k k 场考试分数从高到低完成对这些学生(矩阵中的行)的排序。
返回排序后的矩阵。

数据约束

  • m = s c o r e . l e n g t h m = score.length m=score.length
  • n = s c o r e [ i ] . l e n g t h n = score[i].length n=score[i].length
  • 1 ≤ m , n ≤ 250 1 \le m, n \le 250 1m,n250
  • 1 ≤ s c o r e [ i ] [ j ] ≤ 105 1 \le score[i][j] \le 105 1score[i][j]105
  • s c o r e score score不同 的整数组成
  • 0 ≤ k < n 0 \le k \lt n 0k<n

解题过程

根据某个标准带着整个数组排序,可以当作模板记下来。
题目保证待排序的元素不重复,那就可以完全不考虑稳定性的问题。
写一下在其它算法中常用 O ( N l o g N ) O(NlogN) O(NlogN) 量级的简单做法,再把三种常用排序都实现一下当作练习好了。

具体实现

调用 API

class Solution {public int[][] sortTheStudents(int[][] score, int k) {Arrays.sort(score, (o1, o2) -> o2[k] - o1[k]);return score;}
}

归并排序 - 递归版

class Solution {// 二维数组最大长度为 250,开长为 300 的辅助数组就够了private static final int MAX_N = 300;private static final int[][] temp = new int[MAX_N][];private int k;public int[][] sortTheStudents(int[][] score, int k) {this.k = k;mergeSort(score, 0, score.length - 1);return score;}// 归并操作,入参改成二维数组private void merge(int[][] arr, int left, int mid, int right) {int index1 = left, index2 = mid + 1, index = left;while(index1 <= mid && index2 <= right) {// 除了收集元素的标准不一样,其它都可以不变temp[index++] = arr[index1][k] > arr[index2][k] ? arr[index1++] : arr[index2++];}while(index1 <= mid) {temp[index++] = arr[index1++];}while(index2 <= right) {temp[index++] = arr[index2++];}System.arraycopy(temp, left, arr, left, right - left + 1);}// 归并排序,入参改成二维数组private void mergeSort(int[][] arr, int left, int right) {if(left == right) {return;}int mid = left + ((right - left) >>> 1);mergeSort(arr, left, mid);mergeSort(arr, mid + 1, right);merge(arr, left, mid, right);}
}

归并排序 - 非递归版

class Solution {// 二维数组最大长度为 250,开长为 300 的辅助数组就够了private static final int MAX_N = 300;private static final int[][] temp = new int[MAX_N][];private int k;public int[][] sortTheStudents(int[][] score, int k) {this.k = k;mergeSort(score);return score;}// 归并操作,入参改成二维数组private void merge(int[][] arr, int left, int mid, int right) {int index1 = left, index2 = mid + 1, index = left;while(index1 <= mid && index2 <= right) {// 除了收集元素的标准不一样,其它都可以不变temp[index++] = arr[index1][k] > arr[index2][k] ? arr[index1++] : arr[index2++];}while(index1 <= mid) {temp[index++] = arr[index1++];}while(index2 <= right) {temp[index++] = arr[index2++];}System.arraycopy(temp, left, arr, left, right - left + 1);}// 归并排序,入参改成二维数组private void mergeSort(int[][] arr) {int n = arr.length;for(int left, mid, right, step = 1; step < n; step <<= 1) {left = 0;while(left < n) {mid = left + step - 1;if(mid >= n - 1) {break;}right = Math.min(left + (step << 1) - 1, n - 1);merge(arr, left, mid, right);left = right + 1;}}}
}

随机快速排序

class Solution {private static int k;private static int first, last;public int[][] sortTheStudents(int[][] score, int k) {this.k = k;quickSort(score, 0, score.length - 1);return score;}// 交换操作,入参改成二维数组private void swap(int[][] arr, int i, int j) {int[] temp = arr[i];arr[i] = arr[j];arr[j] = temp;}// 划分操作,入参改成二维数组private void partition(int[][] arr, int left, int right, int pivot) {first = left;last = right;int cur = left;while (cur <= last) {if (arr[cur][k] == pivot) {cur++;// 修改区域标准,较大的数往数组左侧交换} else if (arr[cur][k] > pivot) {swap(arr, first++, cur++);} else {swap(arr, cur, last--);}}}// 随机快排,入参改成二维数组private void quickSort(int[][] arr, int left, int right) {if (left >= right) {return;}int pivot = arr[left + (int) (Math.random() * (right - left + 1))][k];partition(arr, left, right, pivot);quickSort(arr, left, first - 1);quickSort(arr, last + 1, right);}
}

堆排序

class Solution {private int k;public int[][] sortTheStudents(int[][] score, int k) {this.k = k;heapSort(score);return score;}// 交换操作,入参改成二维数组private void swap(int[][] arr, int i, int j) {int[] temp = arr[i];arr[i] = arr[j];arr[j] = temp;}private void downAdjust(int[][] arr, int cur, int size) {int child = 2 * cur + 1;while (child < size) {// 修改确定修改目标的条件,用小根堆来完成排序,就能得到从大到小的结果int target = child + 1 < size && arr[child + 1][k] < arr[child][k] ? child + 1 : child;target = arr[target][k] < arr[cur][k] ? target : cur;if (target == cur) {break;}swap(arr, target, cur);cur = target;child = 2 * cur + 1;}}// 建堆操作,入参改成二维数组private void buildHeap(int[][] arr) {int n = arr.length;for (int i = n - 1; i >= 0; i--) {downAdjust(arr, i, n);}}// 堆排序,入参改成二维数组private void heapSort(int[][] arr) {buildHeap(arr);int size = arr.length;while (size > 0) {swap(arr, 0, --size);downAdjust(arr, 0, size);}}
}

总结梳理

Java 中的排序 API 的实现是 Tim Sort,大体上可以理解为在数据量较小的情况下使用 插入排序,通常使用归并排序。这里表现出来的效率不如直接实现的归并排序,猜想是因为整体数据量不是很大,在某些样例上被忽悠使用了效率不是那么高的算法
归并排序 能够保证时间复杂度在 O ( N l o g N ) O(NlogN) O(NlogN) 这个量级的同时,算法本身是稳定的,是有必要自己实现排序算法时的首选,只需要考虑开辅助数组会不会影响效率。
快速排序 不仅需要额外的系统栈空间,还不稳定。它有时会成为面试时手撕算法的考题,需要好好掌握。
堆排序 是一种原地算法,但是不稳定,所以通常不是一个好的排序算法的选择。但是堆本身能够维护一系列元素中的最大值或者最小值,是一种非常好用的数据结构


http://www.ppmy.cn/embedded/148462.html

相关文章

基于Spring Boot的远程教育网站

一、系统背景与意义 随着互联网技术的飞速发展和普及&#xff0c;远程教育已成为现代教育体系中的重要组成部分。它打破了时间和空间的限制&#xff0c;让学习者可以随时随地进行学习。基于Spring Boot的远程教育网站正是为了满足这一需求而设计的&#xff0c;它利用互联网技术…

嵌入式Linux QT+OpenCV基于人脸识别的考勤系统 项目

此项目是基于人脸识别的考勤系统开发&#xff0c;包括如下模块&#xff1a; 1、人脸识别考勤系统GUI界面设计&#xff0c;包括&#xff1a; &#xff08;1&#xff09;Qt环境(window环境/linux环境) &#xff1b; &#xff08;2&#xff09;Qt工程创建分析&#xff1b; &am…

Redis问题篇

问题 Q1&#xff1a;WRONGTYPE Operation against a key holding the wrong kind of value A1: redis中已经存在同名&#xff0c;但不一样类型的key值

Kibana:LINUX_X86_64 和 DEB_X86_64两种可选下载方式的区别

最近需要在vm&#xff08;操作系统是 Ubuntu 22.04.4 LTS&#xff0c;代号 Jammy。这是一个基于 x86_64 架构的 Linux 发行版&#xff09;上安装一个7.17.8版本的Kibana&#xff0c;并且不采用docker方式。 在下载的时候发现有以下两个选项&#xff0c;分别是 LINUX_X86_64 和 …

三维测量与建模笔记 - 7.3 表面建模概念和方法

基本概念 当我们通过3D扫描设备对物体进行扫描后&#xff0c;会得到三维点云&#xff0c;通过表面建模&#xff0c;我们会重建出物体的3D模型。如果想得到完整的物体的3D模型&#xff0c;需要对物体进行多个角度的扫描并通过拼接算法重建。经过处理得到的3D模型&#xff0c;在很…

clickhouse一直重启,日志提示structure needs cleaning

发现测试环境的clickhouse一直重启&#xff0c;日志报错structure needs cleaning&#xff0c;进入clickhouse的数据目录&#xff0c;执行命令find ./ -name aaa&#xff0c;发现确实存在文件夹损坏的情况。通过重命名损坏文件夹&#xff0c;clickhouse可以正常启动。 类似情况…

Java内区域详解

方法区 方法区属于是 JVM 运行时数据区域的一块逻辑区域&#xff0c;是各个线程共享的内存区域。 《Java 虚拟机规范》只是规定了有方法区这么个概念和它的作用&#xff0c;方法区到底要如何实现那就是虚拟机自己要考虑的事情了。也就是说&#xff0c;在不同的虚拟机实现上&am…

【第九节】Git 服务器搭建

目录 前言 一、 使用裸存储库搭建 Git 服务器 1.1 安装 Git 1.2 创建裸存储库 1.3 配置 SSH 访问 1.4 克隆仓库 二、 使用 GitLab 搭建 Git 服务器 2.1 安装 GitLab 2.2 配置 GitLab 2.3 创建项目 2.4 生成 SSH 密钥 2.5 添加 SSH Key 三、 使用 GitLab 管理项目 …