算法 2.3 分治算法:排序矩阵查找

2021-01-29  本文已影响0人  珺王不早朝

题目描述

给定 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。

数据结构

算法思维

解题要点

关键知识点:分治法(Divide-and-conquer)

  • 首先选择一个元素 6
  • 将数组中的元素划分为 大于6 和 小于6 的两部分
  • 然后问题就变成了两个小数组的排序问题
  • 我们可以继续对小数组采取分治策略,划分成更小的数组
  • 当子数组中元素个数为 1 的时候无需排序

解题步骤


一. Comprehend 理解题意
排序矩阵查找
二. Choose 选择数据结构与算法
数据结构选择
算法思维选择
public boolean searchMatrix(int[][] matrix, int target) {
    for(int i=0; i<matrix.length; i++)
        for(int j=0; j<matrix[0].length; j++)
            if(matrix[i][j] == target)
                return true;
    return false; 
}
三. Code 编码实现基本解法
解题思路剖析

一、分解:矩阵被划分成4个子问题
    可以看到绿色部分一定小于目标元素
    红色部分一定大于目标元素
    只需在剩下的两个行列升序的矩阵中寻找目标元素

二、子问题分别求解
    子问题元素个数为0或者最小元素大于目标返回 false
    否则对两个子矩阵继续递归分解

三、合并
    任一子矩阵内寻找到目标元素,则返回 true

代码实现
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        if (m == 0 ) return false;//行数
        int n = matrix[0].length;
        if (n == 0) return false;// 列数
        return searchSubMatrix(matrix, target, 0, 0, m - 1, n - 1);
    }

    public boolean searchSubMatrix(int[][] matrix, int target, int startRow, int startColumn, int endRow, int endColumn) {
        //结束条件 元素个数小于1或者 矩阵最小元素大于目标值(该矩阵所有元素都大于目标值)
        if(startRow>endRow||startColumn>endColumn|| matrix[startRow][startColumn]>target)
            return false;
        int diagonal_length = Math.min(endRow - startRow + 1, endColumn - startColumn + 1);
        for (int i = 0; i < diagonal_length; i++) {
            //函数主功能:在对角线上查找元素 、分解问题、递归求解、合并
            if (matrix[startRow + i][startColumn + i] == target) 
            return true;
            if (i == diagonal_length - 1 || matrix[startRow + i + 1][startColumn + i + 1] > target) {
                //找到了分界点,寻找4个区域中剩下的两个 (右上、左下)
                return searchSubMatrix(matrix, target, startRow, startColumn + i + 1, startRow + i, endColumn) 
                    || searchSubMatrix(matrix, target, startRow + i + 1, startColumn, endRow, startColumn + i);
            } 
        }
        return false; 
    }
}

时间复杂度 O((m+n)*log(m+n))
  • 从矩阵左上方开始不断比较目标值与对角线元素,寻找划分位置
  • 如果未找到目标值,接着继续在两个子矩阵重复这个过程(递归)
  • 时间复杂度为 O((m+n)*log(m+n))

空间复杂度 O(log(m+n))
  • 需常数级临时变量
  • 递归调用占用额外空间,递归深度为 log(m+n)
  • 因此空间复杂度为 O(log(m+n))

执行耗时:7 ms,击败了 46.64% 的Java用户
内存消耗:44.2 MB,击败了 67.48% 的Java用户

四. Consider 思考更优解
寻找更好的算法思维
算法思维选择
五. Code 编码实现最优解
解题思路剖析
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length; 
        if (m == 0) return false;//行数
        int n = matrix[0].length;
        if (n == 0) return false;// 列数
        //初始位置右上角元素
        int currentRow = 0;
        int currentColumn = n - 1;
        
        while (currentColumn >= 0 && currentRow < m) {
            //当前元素等于目标值,返回true
            if (matrix[currentRow][currentColumn] == target) 
                return true;
            //若当前元素小于目标值 指针下移
            if (matrix[currentRow][currentColumn] < target) {
                currentRow++;
            } else {//若当前元素大于目标值 指针左移
                currentColumn--; 
            }
        }
        return false; 
    }
}

时间复杂度:O(m+n)
  • 最多比较 m+n-1 次,时间复杂度为 O(m+n)

空间复杂度:O(1)
  • 临时变量,常数级内存空间占用,空间复杂度为 O(1)

执行耗时:6 ms,击败了 100.00% 的Java用户
内存消耗:44.4 MB,击败了 28.11% 的Java用户

六. Change 变形与延伸
题目变形
延伸扩展
上一篇下一篇

猜你喜欢

热点阅读