本文已参与「新人创作礼」活动,一起开启掘金创作之路。
题目链接: leetcode.com/problems/th…
1. 题目介绍(矩阵中最弱的K行)
You are given an m x n binary matrix mat of 1's (representing soldiers) and 0's (representing civilians). The soldiers are positioned in front of the civilians. That is, all the 1's will appear to the left of all the 0's in each row.
【Translate】: 你将得到一个m × n的二进制矩阵,其中1(代表士兵)和0(代表平民)。士兵们站在平民的前面。也就是说,每一行中所有的1都出现在所有0的左边。
A row i is weaker than a row j if one of the following is true:
【Translate】: 如果下列任一项成立,那么我们就说第i行就比第j行弱
- The number of soldiers in row i is less than the number of soldiers in row j.(第i行士兵的数量小于第j行士兵的数量)
- Both rows have the same number of soldiers and i < j.(这两行有相同数量的士兵,且i < j)
Return the indices o f the k weakest rows in the matrix ordered from weakest to strongest. 【Translate】: 按照从弱到强的顺序返回矩阵中k个最弱行的索引。
测试用例:
约束:
2. 题解
2.1 类似选择排序的思想
首先我们通过遍历提供的矩阵,确定了每一行soldiers的数量;之后借助选择排序的思想,每次从中找到一个最小值,将它的下标记录下来。与选择排序不同的是,在这里只是比较出最小值,但不交换,而是用最大值替代。
class Solution {
public int[] kWeakestRows(int[][] mat, int k) {
int n = mat.length;
int[] weakest = new int[n];
int[] copy = new int[n];
int[] weak = new int[k];
// 确定矩阵中每一行soldiers的数量
for(int i = 0; i< n; i++){
for(int j = 0; j<mat[0].length; j++){
if(mat[i][j] == 1){
weakest[i] += 1;
}
}
}
// 遍历确定最小值下标
for (int i = 0; i < n; i++) {
int min = 101;
int minIndex = 0;
for (int j = 0; j < n ; j++) {
if (min > weakest[j]) {
min = weakest[j];
minIndex = j;
}
}
copy[i] = minIndex;
weakest[minIndex]=101;
}
// 根据k值裁剪copy数组
for(int i = 0; i < k; i++){
weak[i] = copy[i];
}
return weak;
}
}
2.2 简化2.1的方法
Most Vote中的题解,与2.1相同的思想,但更简洁。其没有对整个矩阵进行记录,而是根据题目需要的K值进行记录。
public int[] kWeakestRows2(int[][] mat, int k) {
int rows = mat.length;
int cols = mat[0].length;
//Create an array of size no. of rows present in mat
int arr[] = new int[rows];
for(int i = 0; i < rows; i++){
int count1 = 0;
for(int j = 0; j < cols; j++){
if(mat[i][j] == 1){
count1++;
}
}
arr[i] = count1;
}
int ans[] = new int[k];//Create ans array of size k
for(int i = 0; i < k; i++){
int min = Integer.MAX_VALUE;
int minIndex = 0;
for(int j = 0; j < arr.length; j++){
if(arr[j] < min){
min = arr[j];
minIndex = j;
}
}
ans[i] = minIndex;
arr[minIndex] = Integer.MAX_VALUE;
}
return ans;
}
2.3 HashMap的方法
Most Vote中使用HashMap方法的题解,但并不高效,稍微了解一下就可以了。
public int[] kWeakestRows3(int[][] mat, int k) {
Map<Integer,Integer> map=new HashMap<>();
int res[]=new int[k];
for(int i=0;i<mat.length;i++){
int count=0;
for(int j=0;j<mat[i].length;j++){
if(mat[i][j]==1)
count++;
else break;
}
map.put(i,count);
}
List<Map.Entry<Integer,Integer>> list=new LinkedList<Map.Entry<Integer,Integer>>(map.entrySet());
Collections.sort(list,new Comparator<Map.Entry<Integer,Integer>>(){
public int compare(Map.Entry<Integer,Integer> a,Map.Entry<Integer,Integer> b){
if(a.getValue()==b.getValue())
return a.getKey()-b.getKey();
return a.getValue()-b.getValue();
}
});
int i=0;
for(Map.Entry<Integer,Integer> mp:list)
{
res[i++]=mp.getKey();
if(i==k)
break;
}
return res;
}
2.4 List列表数组的方法
该方法我个人认为是十分巧妙的,它没有像常规思路那样,使用一个数组来记录矩阵每行的soldiers数量,而是直接申请开辟一个最大soldiers数量长的列表,根据列表下标一一对应soldiers数量,list[0]用来保存矩阵中soldiers数为0的行号,list[1]用来保存矩阵中soldiers数为1的行号,以此类推(由于list存放的是一个对象的地址,而不是这个对象的值,所以可以先赋值,后改变,因为一开始赋值得到的是地址,后续数值改变了,但是地址不变,我们通过这个地址就可以得到修改后的数值);最后按照顺序将相应的weakers取出即可。
public int[] kWeakestRows4(int[][] mat, int k) {
List<Integer>[] map = new List[mat[0].length + 1];
// 对矩阵每行的soldiers数量以map的下标进行排列,如map[0]中要保存的是0个soldier的矩阵行数
for (int i = 0; i < mat.length; i++) {
int count = 0;
for (int j = 0; j < mat[0].length; j++) {
if(mat[i][j] == 1) {
count++;
} else {
break;
}
}
// temp作为临时变量,用来辅助map保存对应数量的矩阵行数
List<Integer> temp = map[count];
if(temp == null) {
temp = new ArrayList();
map[count] = temp;
}
temp.add(i);
// System.out.println(i + ", " + count);
}
int[] result = new int[k];
int i = 0;
int j = 0;
while(i < k) {
while(map[j] == null) j++;
List<Integer> temp = map[j++];
// System.out.println(temp);
for(int t = 0; t < temp.size() && i < k; t++, i++) {
result[i] = temp.get(t);
}
}
return result;
}
2.5 PriorityQueue 堆的方法
public int[] kWeakestRows5(int[][] mat, int k) {
// 不等,小根堆排列;相等,大根堆排列
PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]);
int pos=0;
for (int[] row : mat){
int lo = 0, hi = row.length;
while(lo < hi){
int mid = (lo + hi) / 2;
if (row[mid] != 0) lo = mid + 1;
else hi = mid;
}
q.add(new int[]{lo, pos++});
}
int[] output = new int[k];
for(int i = 0; i < k; i++) output[i] = q.remove()[1];
return output;
}
3. 可参考
[3] Java算法之选择排序