摘要:
学习leetcode_240: search-a-2d-matrix-ii的解法:学习矩阵的二分查找(二维)。
正文:
1、问题描述
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:
- Integers in each row are sorted in ascending from left to right.
- Integers in each column are sorted in ascending from top to bottom.
Example:
Consider the following 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]
]
Given target = 5
, return true
.
Given target = 20
, return false
.
(**分析**)
思路1:分解为一维的二分查找
由于每一行、每一列都已经是排好序了的,那么可以进行m个length=n的二分查找(或n个length=m的二分查找)。算法复杂度大概是O(nlogn),缺陷很明显,只用到了一个维度的排序信息,浪费了另一个维度的排序信息。
思路2:从左下或右上开始进行线性单步查找
类似于leetcode_11: container-with-most-water、leetcode_167: two-sum-ii-input-array-is-sorted和leetcode_345: reverse-vowels-of-a-string,题目的“信息结构”能够让一些线性单步查找成为可能(排好序、最大最小等)。在leetcode_240中,左下和右上是一个特殊的起点,以左下为例,如果该值大于target,则其右所有的数都会大于target,因此该行就不再需要检查而直接跳到上一行(i-=1);如果该值小于target,则其上所有的数都会小于target,因此该列就不再需要检查而直接跳到下一列(j+=1)。如此反复,充分利用了两个维度的排序信息,能够以O(m+n)来完成查询。
思路3:分治法+二维二分法
从一个矩阵的中间点来看,如果该点大于target,那么其右下方所有的点都会大于target,而剩余部分可以分解为两个子矩阵继续查找;如果该点小于target,那么其左上方所有点都会小于target。如果先在对角线上找到一点(对角线也是一个排好序的数组),满足条件diag[i]<target<diag[i+1],那么可以将整个矩阵分解为两个子矩阵,然后同样地地分解该子矩阵为两个子矩阵继续查找(递归)。分治法的基线条件是输入为空矩阵时,返回False。如此反复,这样也充分利用了两个维度的排序信息。
2、python代码实现
思路1:
思路2:
思路3:
test:
构建了一个5000X5500的较大矩阵进行查询,查看三个思路的运行时间差异,可以看到,思路2和3的效率确实比思路1要高一个数量级,而思路1肯定也会比暴力搜索快很多。
3、启发
已知的条件和求解目标的形式,很多时候都蕴含了非常有用的信息(以序信息为主),如果能够充分而巧妙地利用,是能够显著增加程序效率的。这不仅会体现在leecode等解题过程中,实际环境里也会常常遇到。这些思路往往是二分、线性、递归等等,需要在学习和工作中多多总结,让代码更有效率。
leetcode_240.pygithub.com