如何将有序矩阵与二叉搜索树联系起来|Java 刷题打卡

简介: 如何将有序矩阵与二叉搜索树联系起来|Java 刷题打卡

网络异常,图片无法展示
|


题目描述



这是 LeetCode 上的 74. 搜索二维矩阵 ,难度为 中等


Tag : 「二叉搜索树」、「二分」


编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:


每行中的整数从左到右按升序排列。 每行的第一个整数大于前一行的最后一个整数。

示例 1:


网络异常,图片无法展示
|


输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true
复制代码


示例 2:


网络异常,图片无法展示
|


输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false
复制代码


提示:


  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 100
  • -10^4104 <= matrix[i][j], target <= 10^4104


二分解法(一)



由于二维矩阵固定列的「从上到下」或者固定行的「从左到右」都是升序的。


因此我们可以使用两次二分来定位到目标位置:


  1. 第一次二分:从第 0 列中的「所有行」开始找,找到合适的行 row
  2. 第二次二分:从 row 中「所有列」开始找,找到合适的列 col


代码:


class Solution {
    public boolean searchMatrix(int[][] mat, int t) {
        int m = mat.length, n = mat[0].length;
        // 第一次二分:定位到所在行(从上往下,找到最后一个满足 mat[x]][0] <= t 的行号)
        int l = 0, r = m - 1;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (mat[mid][0] <= t) {
                l = mid;
            } else {
                r = mid - 1;
            }
        }
        int row = r;
        if (mat[row][0] == t) return true;
        if (mat[row][0] > t) return false;
        // 第二次二分:从所在行中定位到列(从左到右,找到最后一个满足 mat[row][x] <= t 的列号)
        l = 0; r = n - 1;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (mat[row][mid] <= t) {
                l = mid;
            } else {
                r = mid - 1;
            }
        }
        int col = r;
        return mat[row][col] == t;
    }
}
复制代码


  • 时间复杂度:O(\log{m} + \log{n})O(logm+logn)
  • 空间复杂度:O(1)O(1)


二分解法(二)



当然,因为将二维矩阵的行尾和行首连接,也具有单调性。


我们可以将「二维矩阵」当做「一维矩阵」来做。


代码:


class Solution {
    public boolean searchMatrix(int[][] mat, int t) {
        int m = mat.length, n = mat[0].length;
        int l = 0, r = m * n - 1;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (mat[mid / n][mid % n] <= t) {
                l = mid;
            } else {
                r = mid - 1;
            }
        }
        return mat[r / n][r % n] == t;
    }
}
复制代码


  • 时间复杂度:O(\log{(m * n)})O(log(mn))
  • 空间复杂度:O(1)O(1)


抽象 BST 解法



我们可以将二维矩阵抽象成「以右上角为根的 BST」:


网络异常,图片无法展示
|


那么我们可以从根(右上角)开始搜索,如果当前的节点不等于目标值,可以按照树的搜索顺序进行:


  1. 当前节点「大于」目标值,搜索当前节点的「左子树」,也就是当前矩阵位置的「左方格子」,即 y--
  2. 当前节点「小于」目标值,搜索当前节点的「右子树」,也就是当前矩阵位置的「下方格子」,即 x++


代码:


class Solution {
    int m, n;
    public boolean searchMatrix(int[][] mat, int t) {
        m = mat.length; n = mat[0].length;
        int x = 0, y = n - 1;
        while (check(x, y) && mat[x][y] != t) {
            if (mat[x][y] > t) {
                y--;
            } else {
                x++;
            }
        }
        return check(x, y) && mat[x][y] == t;
    }
    boolean check(int x, int y) {
        return x >= 0 && x < m && y >= 0 && y < n;
    }
}
复制代码


  • 时间复杂度:O(m+n)O(m+n)
  • 空间复杂度:O(1)O(1)


拓展



如果你掌握了上述解法的话,你还可以试试这题:


240. 搜索二维矩阵 II


最后



这是我们「刷穿 LeetCode」系列文章的第 No.74 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先将所有不带锁的题目刷完。


在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。


为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour…


在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

相关文章
|
25天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
34 3
|
27天前
|
存储 Java 开发者
HashSet和TreeSet教你重新认识Java集合的无序与有序
【10月更文挑战第14天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了它们分别实现无序和有序存储的机制。通过理解HashSet基于哈希表的无序特性和TreeSet利用红黑树实现的有序性,帮助开发者更好地选择合适的集合类型以满足不同的应用场景。
13 2
|
3月前
|
算法 Java
LeetCode经典算法题:矩阵中省份数量经典题目+三角形最大周长java多种解法详解
LeetCode经典算法题:矩阵中省份数量经典题目+三角形最大周长java多种解法详解
50 6
|
4月前
|
安全 Java 开发者
探索Java内存模型:可见性、有序性和并发
在Java的并发编程领域中,内存模型扮演了至关重要的角色。本文旨在深入探讨Java内存模型的核心概念,包括可见性、有序性和它们对并发实践的影响。我们将通过具体示例和底层原理分析,揭示这些概念如何协同工作以确保跨线程操作的正确性,并指导开发者编写高效且线程安全的代码。
|
5月前
|
存储 Java 测试技术
滚雪球学Java(67):深入理解 TreeMap:Java 中的有序键值映射表
【6月更文挑战第21天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
52 2
滚雪球学Java(67):深入理解 TreeMap:Java 中的有序键值映射表
|
4月前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
75 1
|
4月前
|
安全 Java 开发者
Java面试题:Java内存模型解析,Java内存模型的基本概念和它的重要性,Java内存模型中的“可见性”和“有序性”,以及具体实现?
Java面试题:Java内存模型解析,Java内存模型的基本概念和它的重要性,Java内存模型中的“可见性”和“有序性”,以及具体实现?
56 1
|
5月前
|
缓存 Java 程序员
Java内存模型深度解析:可见性、有序性和原子性
在多线程编程中,正确理解Java内存模型对于编写高效且无bug的并行程序至关重要。本文将深入探讨JMM的三大核心特性:可见性、有序性和原子性,并结合实例分析如何利用这些特性来避免常见的并发问题。
55 1
|
5月前
|
算法 Java
[Java·算法·中等] LeetCode21. 合并两个有序链表
[Java·算法·中等] LeetCode21. 合并两个有序链表
52 2
|
5月前
|
存储 Java
打破常规!HashSet和TreeSet教你重新认识Java集合的无序与有序
【6月更文挑战第17天】Java集合框架中的Set接口,HashSet无序而TreeSet有序。HashSet基于哈希表,元素插入顺序不可预测,适合快速去重。TreeSet利用红黑树保证有序性,支持自然排序或自定义排序。若需同时无序和有序,可先用HashSet去重,再将元素加入TreeSet,但会牺牲性能。选择时依据对顺序和性能的需求。
153 2