大厂面试高频题详解:包裹黑色像素点的最小矩形

简介: 大厂面试高频题详解:包裹黑色像素点的最小矩形

一个由二进制矩阵表示的图,0 表示白色像素点,1 表示黑色像素点。黑色像素点是联通的,即只有一块黑色区域。像素是水平和竖直连接的,给一个黑色像素点的坐标 (x, y) ,返回囊括所有黑色像素点的矩阵的最小面积。

在线评测地址:领扣题库官网

样例 1:

输入:["0010","0110","0100"],x=0,y=2
输出:6
解释:
矩阵左上角坐标是(0, 1), 右下角的坐标是(2, 2)

样例 2:

输入:["1110","1100","0000","0000"], x = 0, y = 1
输出:6
解释:
矩阵左上角坐标是(0,  0), 右下角坐标是(1, 3)

算法:二分

  • 关于BFS和DFS
    BFS和DFS的做法就是遍历这个黑色像素连通块,得到各个方向上的坐标极值,时间复杂度O(K),K为黑色像素点个数,这种做法在黑色像素点数量巨大时效率极低。

另外关于BFS和DFS如何选择,这里建议使用BFS,因为DFS会占用大量的系统栈,空间复杂度上要劣于BFS
下面我们来介绍一种在大部分情况下空间和时间上均优于DFS和BFS的算法——二分

算法思路

  • 二分找到四个方向上黑色像素点出现的坐标极值

代码思路

  • 这边以二分最左侧黑色像素为例
  1. 设置左指针为0,右指针为y,因为我们保证y列上存在黑色像素,最左侧黑色像素所在列一定在y或者其左侧
  2. 若mid所在列存在黑色像素,说明最左侧黑色像素在mid列或者其左侧,r = mid,否则l = mid
  3. 判断l列是否存在黑色像素,若存在则left = l,否则left = r。注意一定要先判l列,因为r可能存在黑色像素,但并不是最左侧
  • 以此类推继续找到最右侧,最上侧,最下侧的黑色像素所在列或行
  • 计算面积(right - left + 1) * (bottom - top + 1)并将其返回
  • 这里提一种优化,找到最左处和最右处的黑色像素位置left和right后,在找最上和最下坐标时,对于行的判断只需要扫描[row,left]到[row,right]即可

复杂度分析

  • 空间复杂度:O(1)
  • 时间复杂度:O(MlogN+NlogM)(最坏情况)
    最坏情况即只有一个黑色像素点,那么每次判断列上或行上是否又黑色像素点需要扫描完整列或整行

二分的做法当遇到黑色像素点很少且给出的矩阵很大时效率会变得极低,而此时BFS的效率会相对高很多
后记
能否做到在任何情况下效率都显得相对较高呢?我们可以设定一个阈值cnt,先进行BFS遍历,当遍历次数达到cnt时改用二分法进行计算

public class Solution {

    /**
     * @param image: a binary matrix with '0' and '1'
     * @param x: the location of one of the black pixels
     * @param y: the location of one of the black pixels
     * @return: an integer
     */

    public int minArea(char[][] image, int x, int y) {
        if (image == null || image.length == 0 || image[0].length == 0) {
            return 0;
        }

        int n = image.length;
        int m = image[0].length;
        int l = 0, r = 0;
        int left = 0, right = 0, top = 0, bottom = 0;
        //二分最左侧黑色像素坐标
        l = 0;
        r = y;
        while (l + 1 < r) {
            int mid = l + (r - l) / 2;
            if (check_column(image, mid)) {
                r = mid;
            } else {
                l = mid;
            }
        }
        if (check_column(image, l)){
            left = l;
        }else{
            left = r;
        }
        //二分最右侧黑色像素坐标
        l = y;
        r = m - 1
        while (l + 1 < r) {
            int mid = l + (r - l) / 2;
            if (check_column(image, mid)) {
                l = mid;
            } else {
                r = mid;
            }
        }
        if (check_column(image, r)) {
            right = r;
        }else{
            right = l;
        }
        //二分最上侧黑色像素坐标
        l = 0;
        r = x;
        while (l + 1 < r) {
            int mid = l + (r - l) / 2;
            if (check_row(image, mid, left, right)) {
                r = mid;
            } else {
                l = mid;
            }
        }       

        if (check_row(image, l, left, right)) {
            top = l;
        }else{
            top = r;
        }
        //二分最下侧黑色像素坐标
        l = x;
        r = n - 1;
        while (l + 1 < r) {
            int mid = l + (r - l) / 2;
            if (check_row(image, mid, left, right)) {
                l = mid;
            } else {
                r = mid;
            }
        }      

        if (check_row(image, r, left, right)) {
            bottom = r;
        }else{
            bottom = l;
        }
        return (right - left + 1) * (bottom - top + 1);
    }

    //判断列上是否存在黑色像素
    private boolean check_column(char[][] image, int col) {
        for (int i = 0; i < image.length; i++) {
            if (image[i][col] == '1') {
                return true;
            }
        }
        return false;
    }
    //判断行上是否存在黑色像素
    private boolean check_row(char[][] image, int row ,int left ,int right) {
        for (int j = left; j <= right; j++) {
            if (image[row][j] == '1') {
                return true;
            }
        }
        return false;
    }
}

更多题解参考:九章官网solution

相关文章
|
存储 自然语言处理 算法
ES高频面试问题:一张图带你读懂 Elasticsearch 中“正排索引(正向索引)”和“倒排索引(反向索引)”区别
ES高频面试问题:一张图带你读懂 Elasticsearch 中“正排索引(正向索引)”和“倒排索引(反向索引)”区别
ES高频面试问题:一张图带你读懂 Elasticsearch 中“正排索引(正向索引)”和“倒排索引(反向索引)”区别
|
5月前
|
存储 SQL 算法
LeetCode面试题84:柱状图中最大的矩形
LeetCode面试题84:柱状图中最大的矩形
|
6月前
|
前端开发 开发者
CSS面试考点:盒模型、选择器、单位和像素概念
【4月更文挑战第2天】 CSS面试考点:盒模型、选择器、单位和像素概念
52 12
|
前端开发 容器
🍊Flex布局最佳实践之骰子实战篇(面试高频考点,速来围观呀~)
🍊Flex布局最佳实践之骰子实战篇(面试高频考点,速来围观呀~)
641 6
🍊Flex布局最佳实践之骰子实战篇(面试高频考点,速来围观呀~)
|
消息中间件 NoSQL Java
高频Java面试题集锦(含答案),让你的面试之路畅通无阻!
或许这份面试题还不足以囊括所有 Java 问题,但有了它,我相信你一定不会“败”的很惨,因为有了它,足以应对目前市面上绝大部分的 Java 面试了,因为这篇文章不论是从深度还是广度上来讲,都已经囊括了非常多的知识点了。 凡事预则立,不预则废。能读到这里的人,我相信都是这个世界上的“有心人”,还是那句老话:上天不负有心人!我相信你的每一步努力,都会收获意想不到的回报。
|
存储 缓存 NoSQL
Redis缓存穿透和雪崩相关概念(面试高频,工作常用)
Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面,但同时,它也带来了一些问题,其中,最重要的问题,就是数据的一致性问题。从严格意义上讲,这个无解。如果对数据的一致性要求很高,那么就不能使用缓存。
153 0
Redis缓存穿透和雪崩相关概念(面试高频,工作常用)
页面中有父子组件,生命周期顺序如何执行?(面试高频 一篇搞懂)
在实际开发中,一个页面经常会有父组件和子组件,那么在这个页面中,对于父子组件他们的生命周期又是如何执行的呢?看了一些网上的文章资料,竟然有些讲解写的是错误答案,带偏大家,所以在本文利用实践得出结论,带大家彻底搞懂这个知识点~
155 0
页面中有父子组件,生命周期顺序如何执行?(面试高频 一篇搞懂)
|
算法 Java
Map与Set高频面试算法题(只出现一次的数字,复制带随机指针的链表,宝石与石头,旧键盘,前k个高频单词)(Java实现)
给一个非空整数数组,只有一个元素出现了一次,剩余的元素都出现了两次,,请找出那个只出现一次的数字
Map与Set高频面试算法题(只出现一次的数字,复制带随机指针的链表,宝石与石头,旧键盘,前k个高频单词)(Java实现)
|
存储 算法
面试高频算法题---最长回文子串
因此我们可以写出动态规划的状态转移方程:F(i,j) = F(i+1,j-1) && (Si==Sj),此方程表示的意思是:F(i+1,j-1)为回文串并且s的第i个字符和第j个字符相等F(i,j)才是回文串。
面试高频算法题---最长回文子串
|
存储 算法
面试高频算法题---无重复字符的最长子串
给定一个字符串s,请你找出其中不含有重复字符的最长子串长度
面试高频算法题---无重复字符的最长子串