问题描述:
给定一个二维数组,其每行每列都已经按从小到大的顺序排列好了,要求设计一个高效的搜索算法。
思路:
有序、查找,这两个关键字一出现我们马上就会联想到二分查找,但是如何在二维数组(以下称之为矩阵)中进行二分查找呢?
根据题目描述,我们不难推断出这个矩阵的对角线上的元素也是有序排列的。
因此,我们可以先在矩阵的对角线上进行一维的二分查找,如果没有找到目标元素,就返回一个(最好是第一个)比目标元素大的元素。
这时候这个元素就将原矩阵分为了4个部分,其中右下角这个部分一定不包括目标元素(因为肯定比目标元素大),我们继续在剩余3个矩阵重复上述步骤即可。这个方法其实使用到了分治法的思想。
其中,当矩阵含有的元素个数为1时为最小子问题,直接判断即可。当然也可以适当扩大最小子问题的规模。
当然,实现算法的过程中比较复杂的地方是:如果矩阵行和列不相等,怎么确定对角线。我们可以采取比较模糊的方法:只要找到一个比目标元素大的元素即可!
1 | 2 | 3 | 4 |
---|---|---|---|
6 | |||
10 | |||
14 |
例如上表中的矩阵,如果我们要寻找5的话,则首先定位到的元素应该是7(因为1、7,12、17中第一个比5大的元素为7),则7所在行和列包围的右下角子矩阵不可能存在目标元素,所以我们对下面3个子矩阵再进行上述步骤。
左下角子矩阵:
6 |
---|
10 |
14 |
右上角子矩阵
2 | 3 | 4 |
---|
左上角子矩阵
1 |
---|
源代码:
//
// main.cpp
// SearchMatrix
//
// Created by 胡昱 on 2021/11/18.
//
#include <iostream>
using namespace std;
// 类似于分治法
int search(int** matrix, int begin_x, int begin_y, int m, int n, int t) {
// 处理极端情况
if(matrix[begin_x][begin_y] > t || matrix[begin_x + m - 1][begin_y + n - 1] < t) {
return 0;
}
// 处理最小子问题(也就是降低到一维的情况)
if(m * n <= 1) {
return (matrix[begin_x][begin_y]) == t ? 1 : 0;
}
// 处理一般情况
int low = begin_x, high = begin_x + m - 1, m_mid = (low + high) / 2;
int left = begin_y, right = begin_y + n - 1, n_mid = (left + right) / 2;
// 根据类对角线上第一个大于目标元素的中间元素划分子问题(这两个值随便初始化了一下)
int x_mid = begin_x, y_mid = begin_y;
while(low <= high && left <= right) {
if(matrix[m_mid][n_mid] == t) {
return 1;
}
else if(matrix[m_mid][n_mid] < t) {
if(low < high) {
low = m_mid + 1;
}
if(left < right) {
left = n_mid + 1;
}
}
else {
x_mid = m_mid;
y_mid = n_mid;
if(low < high) {
high = m_mid - 1;
}
if(left < right) {
right = n_mid - 1;
}
}
int new_m_mid = (low + high) / 2;
int new_n_mid = (left + right) / 2;
if(new_m_mid == m_mid && new_n_mid == n_mid) {
break;
}
else {
m_mid = new_m_mid;
n_mid = new_n_mid;
}
}
// 求解子问题
// 左上角结果 + 左下角结果 + 右上角结果
int flag = 0;
if(x_mid > begin_x && y_mid > begin_y) {
flag += search(matrix, begin_x, begin_y, x_mid - begin_x, y_mid - begin_y, t);
}
if(x_mid <= begin_x + m - 1 && y_mid > begin_y) {
flag += search(matrix, x_mid, begin_y, begin_x + m - x_mid, y_mid - begin_y, t);
}
if(x_mid > begin_x && y_mid <= begin_y + n - 1) {
flag += search(matrix, begin_x, y_mid, x_mid - begin_x, begin_y + n - y_mid, t);
}
return flag;
}
int main(int argc, const char * argv[]) {
// 共nums组测试
int nums;
cin >> nums;
while((nums--) > 0) {
// 输入矩阵和目标
int m, n ,target;
cin >> m >> n >> target;
int** matrix = new int*[m];
for(int mi = 0; mi < m; ++mi) {
matrix[mi] = new int[n];
for(int ni = 0; ni < n; ++ni) {
cin >> matrix[mi][ni];
}
}
// 开始二维的二分查找
int flag = search(matrix, 0, 0, m, n, target);
// 输出结果并清理资源
cout << (flag > 0 ? "true" : "false") << endl;
for(int mi = 0; mi < m; ++mi) {
delete [] matrix[mi];
}
delete [] matrix;
}
}