《程序员面试金典(第6版)》面试题 10.09. 排序矩阵查找(观察法,二分法,分治算法入门题目,C++)

题目描述

给定M×N矩阵,每一行、每一列都按升序排列,请编写代码找出某元素。

示例:

现有矩阵 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。

解题方法与思路

  • 这道题,确实可以算作一道中等题。它一共有三种解决方案,一种方法的时间复杂度都要低于前面一种的时间复杂度。并且,每一种都有它独特的地方在。我想,这就是这道题的魅力所在。

  • 接下来,就让我来为你讲述,这道题的三种方法究竟是哪三种方法。

方法一:顺序查找法

顾名思义,就是顺序查找,暴力破解。我们用两个双层for循环去解决它。

  • 但是,需要注意的是,使用一般的双层for循环,这道题会超时,得写成类似于这样的形式才行
  • for (const auto& row: matrix)
  • 这是因为这样的写法,就不会去复制数组里的元素值,从而节省了一定的时间。

具体的代码如下:

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.empty()) return false;
        for (const auto& row: matrix) {
            for (int element: row) {
                if (element == target) {
                    return true;
                }
            }
        }
        return false;
    }
};

《程序员面试金典(第6版)》面试题 10.09. 排序矩阵查找(观察法,二分法,分治算法入门题目,C++)_第1张图片

复杂度分析

时间复杂度:O(mn) 其中m是矩阵的行数,n是矩阵的列数
空间复杂度:O(1)

方法二:二分查找法

  • 由于这个矩阵中的元素是有序的,所以我们可以使用二分查找法来降低时间复杂度。
  • 我们分别对矩阵的每一行使用二分查找,去找目标元素,找到了就返回true,否则就是false
  • 二分查找其实就是设置两个指针left 与 right 分别指向数组的头元素与尾元素。然后使用while循环去遍历这整个数组
  • left 要小于等于 right 。我们找到数组的中间元素mid。 假如mid 等于目标元素,返回true,小于目标元素,left = mid +1,否则就是right = mid -1
  • 最后如果都找不到就返回 false

具体的代码如下

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        for(int i = 0; i < matrix.size(); ++i){
            int left = 0;
            int right = matrix[0].size() - 1;
            while(left <= right){
                int mid = (left + right) / 2;
                if(matrix[i][mid] == target) return true;
                else if(matrix[i][mid] < target) left = mid +1;
                else right = mid - 1;
            }
        }
        return false;
    }
};

《程序员面试金典(第6版)》面试题 10.09. 排序矩阵查找(观察法,二分法,分治算法入门题目,C++)_第2张图片

复杂度分析

时间复杂度:O(m * log(n)) 其中m是矩阵的行数,n是矩阵的列数
空间复杂度:O(1)

方法三:基于观察法的查找矩阵算法,Z字查找

  • 我们观察矩阵,发现这个矩阵的横向与纵向的元素其实都是递增的。
  • 根据这个特性,我们可以从矩阵的右上角元素开始遍历。
  • 假如目标元素大于右上角元素,说明这个元素,大于第一行所有元素,下次遍历的时候,就直接少遍历一行,从下一行开始遍历。
  • 假如目标元素小于左上角元素,那就说明,这个元素,直接小于最后一列的所有元素,所以下次就直接少遍历一列。
  • 由此这样遍历,每一次遍历,都能缩小一行或者一列的元素。进一步的缩小了时间复杂度与空间复杂度。

具体的代码如下:

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.empty()) return false;
        int row = 0;
        int col = matrix[0].size() - 1;
        while(row < matrix.size() && col >= 0){
            if(matrix[row][col] > target) --col;
            else if(matrix[row][col] < target) ++row;
            else return true;
        }
        return false;
    }
};

《程序员面试金典(第6版)》面试题 10.09. 排序矩阵查找(观察法,二分法,分治算法入门题目,C++)_第3张图片

复杂度分析

时间复杂度:在这个解决方案中,我们从右上角开始搜索,并在每次迭代中排除一行或一列。在最坏的情况下,我们可能需要遍历所有的行和列,因此时间复杂度为 O(M+N),其中 M 是矩阵的行数,N 是矩阵的列数。

空间复杂度:这个算法没有使用额外的数据结构来存储数据,只使用了几个整数变量(如 row 和 col),因此空间复杂度为 O(1)。

这个优化方案相对于二分查找方案,在时间复杂度上有所改进,而空间复杂度保持不变。

总结

  • 这道题用到了二分查找法与观察法来解决矩阵问题,是一道夯实基础的好题。

  • 觉得我写的还行的小伙伴们请给我点一个赞,点一下关注,谢谢!你们的赞与关注就是对我持续输出优质内容的最大鼓励!

你可能感兴趣的:(面试,矩阵,算法)