leetcode 240. 搜索二维矩阵 II

题目

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。

示例:

现有矩阵 matrix 如下:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

给定 target = 5,返回 true。
给定 target = 20,返回 false。

求解思想

最近不经意看到了这个问题,思考了一下,想到了一个递归分治的求解办法。本来既然已经知道求解方法了,依我的个性,就不准备编码来实现这个算法的。奈何前几天刚总结了部分查找算法,刚回顾了一下二分查找,而这个题目又是以二分查找为基础的升级版,所以就想实现一下代码,提升一下写二分查找代码的熟练度。本来以为知道算法思想,编码应该不困难,但是毕竟是打代码嘛,给机器运行的,再加上细节上的考虑不周,还是遇到了些许小问题(逃)。

下面描述一下算法思想:首先根据题目描述,矩阵满足每行元素从左到右升序,每列元素从上到下也是升序。所以对于满足条件的矩阵的所有子矩阵,存在这样一个事实,该矩阵的所有子矩阵左上角第一个元素是子矩阵中最小的元素,右下角最后一个元素是子矩阵中最大的元素。

  1. 利用上述事实,我们先沿矩阵的对角线 [1, 5, 9, 17, 30] 进行二分查找。以查找元素 15 为例,首先定位到 9 和 17 之间,此时将矩阵划为四个部分,如下图所示,分别令为 A, B, C, D。
    在这里插入图片描述
  2. 对于 A 矩阵来说,9 是 A 矩阵的最大元素,而 15 比 9 还大,所以 15 不可能出现在 A 矩阵。同理,17 是 D 矩阵的最小元素,而 15 比 17 还小,所以 15 也不可能出现在 D 矩阵。即 15 只可能出现在 B 矩阵或 C 矩阵。
  3. 以同样的方式,递归查找 B, C 子矩阵。如果 B, C 子矩阵为行向量或列向量,则直接对 B, C 向量进行普通的二分查找,即可回溯查找结果。

编写代码

算法思想很直观,也很好理解。不过编码需要考虑的面面俱到,而这又是很难的,所以我们需要调试迭代。
C语言的参考代码如下(注释也比较详细,相信理解起来的话问题不大):

#include<stdio.h>
/*由于C语言不能将维数不确定的二维数组作为函数参数进行传递,所以我们需要像如下方式定位元素*/
int index(int* a, int c, int i, int j){//c是矩阵列数 
	return a[i*c + j];
}

bool searchMatrix(int* a, int c,//这两个参数用于定位矩阵元素 
					int i, int j, int m, int n,//搜索的矩阵块,(i,j)为左上角坐标,(m,n)为行列数 
					int target//搜索的目标值 
					){
	if(m == 1 && n == 1){//如果查找范围只有一个元素,则比较一次后就可返回查找结果
		if(index(a,c,i,j) == target) return true;
		else return false;
	}
	if(m == 1){//查找范围为一个行向量
		int low = j,high = j+n-1;
		while(low <= high){
			int mid = (low + high)/2;
			int value = index(a,c,i,mid);
			if(value > target) high = mid - 1;
			else if(value < target) low = mid + 1;
			else return true;
		}
		return false;
	}
	if(n == 1){//查找范围为一个列向量
		int low = i,high = i+m-1;
		while(low <= high){
			int mid = (low + high)/2;
			int value = index(a,c,mid,j);
			if(value > target) high = mid - 1;
			else if(value < target) low = mid +1;
			else return true;
		}
		return false;
	}
    //查找范围为一个矩阵
	int e = m < n? m: n;
	int li = i, lj = j, ri = i+e-1, rj = j+e-1;//定位对角线的起点和终点
	while(li <= ri){
		int mi = (li + ri)/2, mj = (lj + rj)/2;
		int value = index(a,c,mi,mj);
		if(value > target) ri = mi - 1, rj = mj - 1;
		else if(value < target) li = mi + 1, lj = mj + 1;
		else return true;
	}
	//此时li=ri+1;lj=rj+1; 
    //下面就需要把所有的可能情况都考虑到
	if(ri < i) return false;
	else if(li > i+e-1){
		if(m > n) return searchMatrix(a,c,li,j,m-n,n,target);
		else if(m < n) return searchMatrix(a,c,i,lj,m,n-m,target);
		else return false;
	}else return searchMatrix(a,c,li,j,m-(li-i),lj-j,target) || searchMatrix(a,c,i,lj,li-i,n-(lj-j),target); 
}

int main(){
	int a[][5] ={
	{1, 4, 7, 11, 15},
	{2, 5, 8, 12, 19},
	{3, 6, 9, 16, 22},
	{10, 13, 14, 17, 24},
	{18, 21, 23, 26, 30}
	};
	if(searchMatrix((int*)a,5,0,0,5,5,26)) printf("True");
	else printf("False");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值