nc86.矩阵元素查找
1. 思路一: (笨办法, 暴力搜索, 不合要求)
直接遍历二维数组即可.
class Solution {
public:
vector<int> findElement(vector<vector<int> > mat, int n, int m, int x) {
vector<int> res(2);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (mat[i][j] == x) {
res[0] = i, res[1] = j;
return res;
}
}
}
return res;
}
};- 时间复杂度: O(nm). 因为遍历了一遍整个矩阵
- 空间复杂度: O(1). 没有使用数组作为额外空间.
2. 思路二: 从左下往右上找(贪心)
思路一是最笨的暴力办法, 我们没有使用一个条件, 就是该矩阵的每行、每列都是从小到大递增的. 根据这个条件, 我们可以从左下找, 然后这样判断:
- 如果当前位置的数大于目标值: 那么根据该矩阵的特性, 其右边的数只可能更大, 我们往上走一步.
- 如果当前位置的数小于目标值: 那么上边的数只可能更小, 我们往右走.
例如,我们从左下角5开始,寻找目标为7. 算法流程如下:
- 5<7, 说明5上边的一定不是解(比5更小),去掉最左边一列,向右走
- 9>7,说明9右边的一定比9更大,向上走
- 8>7, 8右边的一定比9更大,向上走
- 5<7, 5上边的一定比5更小, 向右走
- 7==7, 找到答案
根据贪心性质, 我们是从左下开始寻找的, 搜索路径只会单调的向右上, 不会往左、往下走, 所以肯定能找到答案.
class Solution {
public:
vector<int> findElement(vector<vector<int> > mat, int n, int m, int x) {
vector<int> res(2);
int i = n-1, j = 0; //左下角
// 不走到外面
while (i >= 0 && j <= m-1) {
if (mat[i][j] > x) i--; //往上走
else if (mat[i][j] < x) j++; // 往右走
else {
// 找到了
res[0] = i, res[1] = j;
return res;
}
}
// 走到边上也没找到, 无解
return res;
}
};- 时间复杂度: O(n+m). 因为遍历过程中, 只是往上和右走, 最坏的情况走n+m步走到边上, 所以最慢的复杂度是n+m.
- 空间复杂度: O(1). 没有使用数组作为额外空间.
3. 思路三: 二分搜索
因为每一行都是递增的, 且我们不知道答案在哪一行, 所以可以暴力枚举每一行, 再逐行对列二分搜索x.
class Solution {
public:
vector<int> findElement(vector<vector<int> > mat, int n, int m, int x) {
vector<int> res(2);
// 枚举i行
for (int i = 0; i < n; i++) {
// 如果第i行最左边都比x大,或者最右边都比x小, 那么答案肯定不在这一行
if (mat[i][0] > x || mat[i][m-1] < x) continue;
int l = 0, r = m-1; // 二分查找左右端点
while (l <= r) {
int mid = (l+r) >> 1;
// 如果中点元素大于x, 答案在左边
if (mat[i][mid] > x) r = mid-1;
// 小于x, 答案在右边
else if (mat[i][mid] < x) l = mid+1;
// 找到了
else {
res[0] = i, res[1] = mid;
return res;
}
}
}
return res;
}
};- 时间复杂度: O(nlogm). 遍历过程中, 每一行都对长度为m的数组二分搜索了一次.
- 空间复杂度: O(1). 没有使用数组作为额外空间.

京公网安备 11010502036488号