什么是【搜索二维矩阵】?
要说【搜索二维矩阵】的前提是,必须先存在这么一个m×n矩阵,如下图
这个矩阵必须有以下的特征
- 每行中的整数从左到右按升序排列。
- 每行的第一个整数大于前一行的最后一个整数。
如果矩阵不满住这些特性,必须先变成一个满足上面特征的矩阵才行。
如果已经满足了上面说的要求,那么现在我们用一个二维数组来表示这个矩阵,他就叫做【二维矩阵】。上图矩阵,变为用数组表示的二维矩阵,如下:
> matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]]
因为想写一个算法,可以在这个二维数组中搜索某个特定的值(也就是下面说的target),所以就给这个算法起名为【二维搜索矩阵】。
已上图为例,我们来看一下如何编写这么一个【二维搜索矩阵】的算法,看一下具体的实现原理。
先上代码:
public class Solution {
public static void main(String[] args) throws InterruptedException{
int[][] a = {
{1, 3, 5, 7},
{10, 11, 16, 20},
{23, 30, 34, 50}
};
int target = 34;
System.out.println(searchMatrix(a, target));
}
public static boolean searchMatrix(int[][] matrix, int target) {
// 数组不存在情况,直接返回false
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return false;
}
int row = matrix.length;
int col = matrix[0].length;
// 将二维数组看作为一维数组后[1,3,5,7,10, 11, 16, 20,23, 30, 34, 50],第一个数字的索引值
int startIndex = 0;
// 将二维数组看作为一维数组后[1,3,5,7,10, 11, 16, 20,23, 30, 34, 50],最后一个数字的索引值
int endIndex = row * col - 1;
while (startIndex + 1 < endIndex) {
int midIndex = (startIndex + endIndex ) / 2; //【1】
if (matrix[midIndex / col][midIndex % col] == target) {
return true;
}else if (matrix[midIndex / col][midIndex % col] < target) {
startIndex = midIndex;
} else {
endIndex = midIndex;
}
}
if (matrix[startIndex / col][startIndex % col] == target) {
return true;
}
if (matrix[endIndex / col][endIndex % col] == target) {
return true;
}
return false;
}
}
上面代码用的的知识点:
- 二分查找法(折半)
每次直接去中间的数字来看是否是目标值,如果比目标值大,就从这个中间数的右侧再去折半查,如果比目标值小,下次就从中间值的左边去折半查,这样每次可以省掉一般的时间。 - 等差数列中间项
例如当第一次查询的中间值16比目标值34小时,那么下次就要从16的右边到最后一个数60去中间的那个数在与目标数作比较。怎么获取16和60二分后的中间值,这里就用到了等差数列求中间项,因为代码中是通过数组的索引,所以每个值都差1,所以看做是等差数列,这样通过获取索引这个等差数列的中间项,然后用通过索引就可以得到对应的真是数字了。(数字本身不满足等差数列,所以使用索引)。 - 取模求余
代码详解
代码整体不难,只要弄明白连个点就行:
1. 索引的使用
把二维数组要看做是一个一维数组,例如代码中的二维数组看作一维数组后是这样子的:
[1,3,5,7,10, 11, 16, 20,23, 30, 34, 50]
代码中的体现为:
> int endIndex = row * col - 1; // 11
然后这12个数字分别对应下标索引就是0-11,在代码中通过startIndex和endIndex设置对应的索引值来作为二分查找的开始点和结束点的位置,例如要查找16到60之间的中间的数字值,就可以先求中间数字的索引值midIndex = (6+11)/2 = 8 ,然后这个索引对应的值就是得6和60之间的那个数字23了。
为什么可以通过索引(6+11)/2获取中间的那个数,是因为索引可以看书是等差数列(索引的每个值比前一个值都大1),只要是等差数列就可以取得任意两项中间的值,也叫作等差数列中项值(代码中【1】处)。
2.如何将通过一维数组获取的索引值映射到实际二维数组中
上面我们求索引值是是把二维数组变成一维数组后的索引,而代码中数组实际还是一个二维数组,那么这个索引怎么最终对应到二维数组中的数值呢?还是用上面的例子,当获取到中间项的索引值midIndex=8后,然后用8转换为二维数组中的索引
因为二维数组每个里面的数值格式是固定的,比如这里的二维数组,每个里面有4个数字。然后用获取的一维数组的中间项索引值midIndex直接取整求余数就行。代码中就表现为
> matrix[midIndex / col][midIndex % col] == target
所以整个算法就是将一个固定长度的二维数组看作以为数组后进行二分查找获取中间值的索引,然后将这个索引值在转为实际二维数组中索引,例如看作一维数组后23的索引为8,转换为二维数组的索引就是[2][0],这样就拿到了23,然后再用23跟目标值进行比较就行了。然后不断的循环这个过程,知道二分到无法再分,找到与目标值匹配的就返回true,否则就代表没找到,返回false就行。