LeetCode刷题之1337 矩阵中战斗力最弱的 K 行

64 篇文章 0 订阅
13 篇文章 0 订阅

给你一个大小为 m * n 的矩阵 mat,矩阵由若干军人和平民组成,分别用 1 和 0 表示。

请你返回矩阵中战斗力最弱的 k 行的索引,按从最弱到最强排序。

如果第 i 行的军人数量少于第 j 行,或者两行军人数量相同但 i 小于 j,那么我们认为第 i 行的战斗力比第 j 行弱。

军人 总是 排在一行中的靠前位置,也就是说 1 总是出现在 0 之前。

示例 1:

输入:mat =
[[1,1,0,0,0],
 [1,1,1,1,0],
 [1,0,0,0,0],
 [1,1,0,0,0],
 [1,1,1,1,1]],
k = 3
输出:[2,0,3]

第一种方法:

题解:咱从列分析发现每一行先出现0的就是战斗力最弱的,由此呢我们可以根据列来分析这个题,让列做外层循环,然后判断行号哪个先出现零,但还需要保存下已经记录过最弱的行号,可以用一个布尔数组来判断是否已经记录过,然后还有一种情况我们需要单独拉出来列举下。

当二维数组为:

[[1,1,0,0,0],

[1,1,1,1,0],

[1,0,0,0,0],

[1,1,0,0,0],

[1,1,1,1,1]]

k=5

当遇到这种情况时,在只用上面判断就会出现bug因为有0的行才4行最后一行判断不到,但这种情况比较特殊,在上面循环结束后我们可已单独对最后一列做一个判断,直接往数组放就可以不过先查看下布尔数组里是否记录过此行是否记录过。

以下是代码:

class Solution {
    public int[] kWeakestRows(int[][] mat, int k) {
       int xlength=mat[0].length;//获取列长度

       int[] newIndex=new int[k];//保存最弱战斗力的行号

       boolean isRepeat[]=new boolean[mat.length];//记录行号是否已经保存过 
       int count=0;//为newIndex的下标

       for(int i=0;i<xlength;i++){
          if(count==k){
              break;
          }
           for(int j=0;j<mat.length;j++){
               if(mat[j][i]==0 && !isRepeat[j] && count<k){
                   newIndex[count]=j;
                   isRepeat[j]=true;
                   count++;
               }
           }           
       }
        if(count!=k){
            for(int i=0;i<mat.length;i++){//newIndex数组还没存满时证明数组中已经没有为0的行了直接对最后一行进行最后添加剩余的行
                if(count==k) break;
                if(count<k && !isRepeat[i]){
                    newIndex[count]=i;
                    count++;
                }
            }
        }

       return newIndex;
    }
}

第二种方法:

   上面这种方案并没有利用二分算法,还是有点遗憾。下面讲解一种利用二分解决此问题的方案,

先利用二分把每一行对应战斗力算出来。并放入我们建立的新数组并放入对应的下标中。

代码如下:

 for(int i=0;i<mat.length;i++){
            int left=0,right=mat[i].length-1;

            while(left<right){

                int mid=(left+right)>>1;

                if(mat[i][mid]==0){//等于0时 右边指针 mid
                    right=mid;
                }else{//等于1是左边指针 mid+1
                    left=mid+1;
                }
            }
            if(left==right&& mat[i][left]==1 && left==mat[0].length-1){
           //考虑到有一种情况当一行中都是1时left和right在最后一位但与实际数量差一位需单独考虑。
                index[i]=i+100*(left+1);
            }else{
                index[i]=i+100*left;
            }
        }

 数组里放的100*left+i 此处是为了能同时存在军人和战斗力的情况下进行排序,这样的话到最后我们直接取余就可以得到下标 输出需要输出的个数。 100*left 为什么会用100因为100取余时正好能把left舍去最关键是题目中 行长度最多为100所以才用100而不用1000,10000。到此,最后我们只需要排个序,在对其取余输出即是答案。

以下是全部代码:

public class LeetCode1337 {
    public static void main(String[] args) {
        int mat[][]={{1,1,1,1,1},
                     {1,0,0,0,0},
                     {1,1,0,0,0},
                     {1,1,1,1,0},
                     {1,1,1,1,1}};
        for(int i : kWeakestRows(mat,3)){
            System.out.println(i);
        }
    }//[[1,1,1,1,1],[1,0,0,0,0],[1,1,0,0,0],[1,1,1,1,0],[1,1,1,1,1]]
    public static int[] kWeakestRows(int[][] mat, int k) {

        int[] index=new int[mat.length];

        for(int i=0;i<mat.length;i++){
            int left=0,right=mat[i].length-1;

            while(left<right){

                int mid=(left+right)>>1;

                if(mat[i][mid]==0){//1 1 1 0 0
                    right=mid;
                }else{
                    left=mid+1;
                }
            }
            if(left==right&& mat[i][left]==1 && left==mat[0].length-1){
                index[i]=i+100*(left+1);
            }else{
                index[i]=i+100*left;
            }
        }

        Arrays.sort(index);

        int sortIndex[]=new int[k];

        for(int i=0;i<k;i++){
            sortIndex[i]=index[i]%100;
        }

        return sortIndex;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值