难度 中等
原题
给你一个下标从 0 开始的整数数组
arr
和一个m x n
的整数 矩阵mat
。arr
和mat
都包含范围[1,m * n]
内的 所有 整数。从下标
0
开始遍历arr
中的每个下标i
,并将包含整数arr[i]
的mat
单元格涂色。请你找出
arr
中在mat
的某一行或某一列上都被涂色且下标最小的元素,并返回其下标i
。示例 1:
输入:arr = [1,3,4,2], mat = [[1,4],[2,3]] 输出:2 解释:遍历如上图所示,arr[2] 在矩阵中的第一行或第二列上都被涂色。示例 2:
输入:arr = [2,8,7,4,1,3,5,6,9], mat = [[3,2,5],[1,4,6],[8,7,9]] 输出:3 解释:遍历如上图所示,arr[3] 在矩阵中的第二列上都被涂色。
题意
典中典之阅读理解
这是做过的最难理解的一道题
题意:给你一个m*n的矩阵mat,矩阵里面是数字1到m*n,并且还有一个数组arr,arr也是1到m*n,但是顺序是乱的,现在从arr中读数字,每读到一个数字把mat中的这个数字涂掉,直到涂到第 i 个数字后涂完的数字刚好在矩阵上形成一条直线,返回 i 。
好吧,写完读完我更迷惑了,那再换个例子
假如这里有个鱼塘,就是图中蓝色的部分,四周黄色的地方就是鱼所在的地方,鱼塘现在结冰了,把鱼塘细分成了9个块,按照arr的数组顺序敲碎对应数字的冰块,这些鱼很笨,只能直着游,不能拐弯或者斜着游,如果从冰块8的位置开始,只能到达9号或者3号,因为它只能直着游
然后我们按照arr的顺序依次打碎冰块,当打碎的冰块形成一条完整的路径时鱼刚好可以游到对岸,此时敲碎的第 i 块冰块就是结果
arr = [2,8,7,4,1,3,5,6,9]
敲掉arr[0],此时鱼进去也到达不了对岸
敲掉arr[1],仍然没有鱼能到达对岸
敲掉arr[2],只要再敲掉9左右边的鱼就能到达对岸,或者敲掉4上下的鱼就能到达对岸
再敲掉arr[3],OHHHHHHHHHHHHHHHHHHHHH
通了!鱼终于游到了对岸!
此时的下标,也就是我们敲掉的第4块冰(第一块下标为0),就是我们的结果,答案是3!
解法
看懂题目的在公屏打个瑞思拜
好的,题意已经讲完了,那开始解题
首先这个题目的难点是,我读到arr[ i ]时,如何确定这个数字在矩阵中的位置,可能第一时间想的是遍历,因为矩阵中的每个数字都是独一无二的,完全可以通过遍历获取到这个数字的坐标。
对应固定的一个矩阵,反复遍历一定不是正确的选择,那能不能专门创建一个表记录矩阵中每个元素的位置,然后遍历数组时直接通过数组中这个元素的值获取元素在矩阵的位置
哈希表
首先我们创建一个空的哈希表,因为我们要通过元素获取元素的位置,所以key就用元素,value就是元素在矩阵的位置,位置用数组表示
Map<Integer , int[ ]> map = new HashMap<>();
逐个从矩阵存入哈希表元素坐标后开始判定
public int firstCompleteIndex(int[] arr, int[][] mat) {
int m = mat.length;//二维数组的长度,就是行数
int n = mat[0].length;//二维数组中每个数组的长度,就是列数
int len = arr.length;//数组arr的长度,这题其实就是m*n
if (m == 1 || n == 1) {
return 0;//如果只有一行或者一列,直接就通了,那首个元素就是结果
}
int[] gridm = new int[m];
//每一行有多少个元素被涂了,如果这一行的元素数和列数相同,说明这一行都被涂了
int[] gridn = new int[n];
//每一列的元素
int maxm = 0;
int maxn = 0;
Map<Integer, int[]> map = new HashMap();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
map.put(mat[i][j], new int[]{i, j});
//创建一个存放每个元素与其坐标的哈希表
}
}
for (int i = 0; i < len; i++) {
int[] arri = map.get(arr[i]);
//读到元素后获取其坐标
gridm[arri[0]]++;
//该元素所在的行已涂的格子数量加1
maxm = Math.max(maxm, gridm[arri[0]]);
//maxm用于统计行最大值,只要有任意一行最大值达到了列数就表明成功
gridn[arri[1]]++;
//该元素所在的列已涂的格子数量加1
maxn = Math.max(maxn, gridn[arri[1]]);
//maxn用于统计列最大值,只要有任意一列最大值达到了行数就表明成功
if (maxn==m||maxm==n) {
//如果有一列的数量达到了行数或者行的数量达到了列数返回当前下标
return i;
}
}
System.out.println("欧西给");
return 0;
}