九章算法

2023-11-04

两个排序的数组A和B分别含有m和n个数,找到两个排序数组的中位数,要求时间复杂度应为O(log (m+n))。

在线评测地址:LintCode 领扣

说明

中位数的定义:

  • 这里的中位数等同于数学定义里的中位数
  • 中位数是排序后数组的中间值。

如果有数组中有n个数且n是奇数,则中位数为A[(n-1)/2]

  • A[(n−1)/2]。

如果有数组中有n个数且n是偶数,则中位数为 (A[n / 2] + A[n / 2 + 1]) / 2

  • (A[n/2]+A[n/2+1])/2.
  • 比如:数组A=[1,2,3]的中位数是2,数组A=[1,19]的中位数是10。

样例1

输入:
A = [1,2,3,4,5,6]
B = [2,3,4,5]
输出: 3.5

样例2

输入:
A = [1,2,3]
B = [4,5]
输出: 3

算法:归并

解题思路

  • 最简单的思路是将两个数组合并,然后返回新数组的中位数。九章算法班中讲过,两个有序数组的合并是经典归并排序算法的一步,我们可以新开一个数组,保存合并后的结果。但是我们这样会做额外的工作,因为我们不必保存整个新数组,只需要知道新数组的中位数即可。
  • 因此,更简便的方法是,使用双指针分别对两个数组遍历,比较两个指针下的元素大小,每次移动更小的指针,通过移动次数确定中位数的位置。

算法流程

  • 令指针p1p2分别指向两个数组,初始指向位置0。我们需要遍历(m + n)/2 + 1次,每次比较两个位置的元素,在第k次比较时,较小的那个值就是两个数组中第k + 1小的数。如果一个指针已经走到了数组末尾,那么移动另一个指针,否则每次将指向较小数的指针后移,直到遍历到中位数。
  • 为了将奇偶情况合并,在代码中用了leftright保存中间值,如果是奇数直接返回right,如果是偶数就返回(left + right) / 2

复杂度分析

  • 复杂度分析:

时间复杂度:O(m+n),mn分别是两个数组的长度。双指针遍历两个数组,指针移动次数是0(m+n)级。

  • 空间复杂度:O(1)。
public class Solution {
    /*
     * @param A: An integer array
     * @param B: An integer array
     * @return: a double whose format is *.5 or *.0
     */
    public double findMedianSortedArrays(int[] A, int[] B) {
        int m = A.length, n = B.length;
        int p1 = 0, p2 = 0;
        int left = -1, right = -1;
        for (int i = 0; i < (m + n) / 2 + 1; i ++){
            left = right;
            // p2 右移
            if (p1 >= A.length || p2 < B.length && A[p1] > B[p2]){
                right = B[p2++];
            }
            // p1 右移
            else {
                right = A[p1++];
            }
        }
        return (m + n) % 2 == 1 ? right : (left + right) / 2.0;
    }
}

算法:二分

解题思路

  • 题目要求时间复杂度为O(log(m+n)),不难想到二分法。双指针方法中,我们一个一个的排除不可能的元素。如果充分利用数组的有序性,就能一半一半的排除。具体来说,假设我们要找第k小数,通过二分,可以每次循环排除掉k/2个数。

算法流程

  • 建立辅助函数getKth,参数有数组AA的起始指针start1和终止指针end1, 相对应的有Bstart2end2,以及整数k,目标是找到A[start1:end1]B[start2:end2]中第k小的元素。
  • 在主程序中,看m + n的奇偶性,并调用getKth函数。如果是奇数,返回数组AB的第(m + n) // 2 + 1小元素;如果是偶数,返回数组AB的第(m + n) // 2小和第(m + n) // 2 + 1小元素的均值。
  • getKth(nums1, start1, end1, nums2, start2, end2, k)函数的实现方法: 如果有数组在[start:end]范围内为空,说明该数组已经排除完毕,第k小的元素一定存在于另一数组中,计算好索引位置返回即可。 如果k为1,说明已经找到第k小的数,那就是A[start1]B[start2]中的较小值,直接返回即可。 定义指针ij,分别指向AB,使得A[start1:i]B[start2:j]的长度分别为k // 2,通过比较A[i]B[j]的大小,我们就可以确定排除哪段元素。 如果A[i] > B[j],说明B[start2:j]不可能为第k小元素。我们对A[start1:end1]B[j + 1:end2]继续送入getKth进行递归,k应该更新为k - (j - start2 + 1)。 * 反之,说明A[start1:i]不可能为第k小元素。我们对A[i + 1:end1]B[start2:end2]继续送入getKth进行递归,k应该更新为k - (i - start1 + 1)

复杂度

  • 时间复杂度:O(log(m+n))。mn分别是两个数组的长度。二分法的复杂度为O(log(m+n))。
  • 空间复杂度:O(1)。
 public class Solution {
    /*
     * @param A: An integer array
     * @param B: An integer array
     * @return: a double whose format is *.5 or *.0
     */
    public double findMedianSortedArrays(int[] A, int[] B) {
       int m = A.length, n = B.length;
        // 如果是奇数
        if ((m + n) % 2 == 1) {
            return getKth(A, 0, A.length - 1, B, 0, B.length - 1, (m + n) / 2  + 1);
        }
        // 如果是偶数    
        int left = getKth(A, 0, A.length - 1, B, 0, B.length - 1, (m + n) / 2);
        int right = getKth(A, 0, A.length - 1, B, 0, B.length - 1, (m + n) / 2 + 1);
        return (left + right) / 2.0;  
    }
    
    private int getKth(int[] A, int start1, int end1, int[] B, int start2, int end2, int k) {
        int len1 = end1 - start1 + 1;
        int len2 = end2 - start2 + 1;
        // 让 len1 的长度小于 len2,这样就能保证如果有数组空了,一定是 len1 
        if (len1 > len2) {
            return getKth(B, start2, end2, A, start1, end1, k);
        }
        // A数组排除完毕
        if (len1 == 0) {
            return B[start2 + k - 1];
        } 
        // 已经找到第k小的数
        if (k == 1) {
            return Math.min(A[start1], B[start2]);
        }
        // 开始二分
        int i = start1 + Math.min(len1, k / 2) - 1;
        int j = start2 + Math.min(len2, k / 2) - 1;

        if (A[i] > B[j]) {
            return getKth(A, start1, end1, B, j + 1, end2, k - (j - start2 + 1));
        }
        else {
            return getKth(A, i + 1, end1, B, start2, end2, k - (i - start1 + 1));
        }
    }
}

快速选择等更多解法见:九章算法

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

九章算法 的相关文章

  • 使用 JPA/ORM 生成数据库模式是一个坏主意吗?

    Salve Part of 另一个问题 答案 https stackoverflow com questions 7595578关于 SO 以及其他声称相同的声明 如果您通过 JPA 更新数据库架构 但通常不是一个好的做法 您是否真的不应该
  • Grizzly 和 Servlet 容器上下文

    我试图在我编写的 在 Grizzly 上运行的 Servlet 中获取一些注入的上下文 例如 Session 或 HttpServletRequest 但我所做的似乎都不起作用 整个过程似乎过早地停止了 并出现以下错误 SEVERE Mis
  • 检索和设置 IntelliJ IDEA 插件开发的拆分窗口设置

    我正在编写一个 IntelliJ IDEA 插件 用于保存打开选项卡的会话 称为选项卡会话 https github com alp82 idea tabsession 这个问题是后续问题IntelliJ IDEA 插件开发 保存选项卡组
  • 是否可以使用检测重新定义核心 JDK 类?

    我想重新定义字节码StackOverflowError构造函数 因此当堆栈溢出发生时我有一个 钩子 我想要做的就是在构造函数的开头插入对我选择的静态方法的单个方法调用 是否有可能做到这一点 您应该能够使用两种方法之一来完成此操作 除非在过去
  • 如何在 Java 中根据 XSD 1.1 验证 XML?

    在 Java 中根据 XML Schema 1 1 验证 XML 文件的最佳方法是什么 我从中获取了代码tutorial http www ibm com developerworks xml library x javaxmlvalida
  • Android - 内容值覆盖现有行

    我正在尝试使用插入值ContentValues 我已将 5 个值插入到 5 列中 运行应用程序后 我只有最后一组值的行ContentValues 前四组未插入 ContentValues cv new ContentValues cv pu
  • 寻找 WebElements,最佳实践

    在我们当前的自动化 使用 Selenium WebDriver Java 中 我们使用 FindBy very广泛地 例如 FindBy css a name bcrumb protected List
  • 如何在JUnit测试中将MockWebServer端口设置为WebClient?

    我在用着spring boot with WebClient 它被自动装配为一个 bean 问题 写一个junit集成测试 我必须使用okhttpMockWebServer 该模拟始终在随机端口上启动 例如localhost 14321 N
  • 如何使用 Gradle 2.10 将 ANTLR 词法分析器语法导入到另一个语法中?

    我一直在和 Terence Parr 一起学习 ANTLR 4权威的 ANTLR 4 参考 到目前为止我一直在使用 Gradle 2 10 及其内置 ANTLR 插件进行跟踪 然而 我在获取一些我从第 4 章第 38 41 页改编的代码以使
  • 将位于 jar 中的文件读取为 java.io.File 对象

    与此类似的问题已发布 但似乎没有一个答案对我的情况有帮助 我正在编写一个程序包 它使用 Google 的凭据来获取 Google Apps 用户 为此 我使用服务帐户 因此为了检索凭据 我需要提供 除其他外 一个 p12 签名文件 Cred
  • splitByWholeSeparatorPreserveAllTokens 和 split 之间的区别

    有什么区别StringUtils splitByWholeSeparatorPreserveAllTokens and String split With splitByWholeSeparatorPreserveAllTokens 我们可
  • Stream#limit 返回的元素是否可以少于预期?

    如果流s下面至少有n元素 流在什么情况下sLimit可能少于n元素 如果有的话 Stream sLimit s limit n 提问原因 在这个答案 https stackoverflow com a 28082107 829571 我读到
  • MAC OS 的 java.awt.Robot 类中出现无头环境错误

    我正在尝试使用 JavaFX 应用程序捕获屏幕截图Robot class 这是我在我的应用程序中使用的代码 Rectangle screenBounds new Rectangle Screen getPrimary getBounds g
  • 如何加快 jar 签名者的速度?

    我使用 ant 来签署我的 jars 以进行网络启动部署 Ant signjar 在 Web 启动签名时非常慢 如何加快签名过程 我找到了一种可能的解决方案 早些时候 在构建脚本 ant signjar 中 按顺序调用所有 jar 我们使用
  • Java中通过FTP创建文件夹层次结构

    Java 是否有现成的功能可以在远程 FTP 服务器上创建文件夹层次结构 Apache Commons 确实提供了 FTP 客户端 但我找不到创建目录层次结构的方法 它确实允许创建单个目录 makeDirectory 但创建整个路径似乎并不
  • 从 AlertDialog 返回值

    我想构建一个函数来创建 AlertDialog 并返回用户输入的字符串 这是我用于创建对话框的函数 如何返回该值 String m Text private String openDialog String title AlertDialo
  • jsf 中的类型未找到属性

    我正在尝试调用 jsf 中使用 primefaces 的属性 但我有错误 500 在托管bean PersonelBean 类型上找不到 我正在使用 hibernate jsf 和 spring PersonelBean java Mana
  • 捕获 XSS(跨站脚本)攻击的最佳正则表达式(Java 中)?

    杰夫实际上在净化 HTML http refactormycode com codes 333 sanitize html 但他的示例是用 C 编写的 而我实际上对 Java 版本更感兴趣 有人有更好的 Java 版本吗 他的示例是否足以直
  • 在Java程序中计算zip文件的md5哈希值

    我有一个 zip 文件 在我的 Java 代码中我想计算 zip 文件的 md5 哈希值 有没有我可以用于此目的的 java 库 一些例子将非常感激 谢谢 几周前我通过这篇文章做到了这一点 http www javalobby org ja
  • JBoss 5 截断 base64 cookie 字符串的尾部 =

    从 JBoss 4 升级到 JBoss 5 后 我注意到最烦人的回归 它截断 base64 cookie 值的尾部等号 我花了很长时间才明白问题不是我的代码而是 JBoss 的 我用 google 搜索了一下 发现这是一个已知的问题issu

随机推荐

  • 如何用Python进行股票预测,数据分析带你从小白开始

    在开始这个话题之前请先记住一句友情提醒 股市有风险 投资需谨慎 我们写这个文章并不是鼓励大家去入市 小编本人也不买股票 我们只是在探索Python在股票分析和预测上面能发挥什么样的作用 对于和数据打交道的数据科学家来说 预测证券市场走势远比
  • C语言常见错误分析

    C语言常见错误分析 错误分类 语法错 逻辑错 运行错 0 忘记定义变量 main x 3 y 6 printf d n x y 1 C语言的变量一定要先定义才能使用 2 输入输出的数据的类型与所用格式说明符不一致 int a 3 float
  • PRML_频率与贝叶斯(一)

    我们从数据中能得到以下信息 总体信息 总体所属分布或者所属的分布族带来的信息 样本信息 从总体中抽样得来的样本给我们提供的信息 以上两种信息进行的统计推断称为经典统计学 它的观点是把样本看成来自具有一定概率分布的总体 先验信息 在抽样之前
  • Echarts.js(二):多系列柱状图

    多系列柱状图大部分与多系列折线图相似 一 简单html布局 简单的html如下
  • SqlServer分页查询

    文章目录 这篇博客讲的是SQL server的分页方法 用的SQL server 2012版本 下面都用pageIndex表示页数 pageSize表示一页包含的记录 并且下面涉及到具体例子的 设定查询第2页 每页含10条记录 首先说一下S
  • 写出质量好软件的75条体会

    1 你们的项目组使用源代码管理工具了么 MVM 应该用 VSS CVS PVCS ClearCase CCC Harvest FireFly都可以 我的选择是VSS 郁也风 公司使用的是VSS 在网上与朋友玩的就是CVS了 咖啡 现在都在用
  • 领域驱动设计:领域事件

    文章目录 领域事件 识别领域事件 领域事件相关案例 领域事件总体架构 领域事件 领域事件是领域模型中非常重要的一部分 用来表示领域中发生的事件 一个领域事件将导致进一步的业务操作 在实现业务解耦的同时 还有助于形成完整的业务闭环 举例来说的
  • 【因子算法】——求一个数的因子、质因子、求两个数的公因子

    下面理清楚一些数学概念 因数 一个数 如果存在可以被它整除的数 则这些数都是该数的因数 规定0没有因数 1的因数是1 其他的比如4的因数有 1 2 4 因子 一个数 如果存在可以被它整除的数且这些数不包括它本身 则这些书都是该数的因子 规定
  • 异常处理:System.Xml.XmlException_缺少根元素

    System Xml XmlException 类型的未处理的异常在System Xml dll中发生 其他 缺少根元素 问题背景 可能是因为强制断电 导致电脑里文件出错 原本运行正常的程序出现报错 缺少根元素 处理方法 删除C Users
  • 使用 sCrypt 实现定期支付合约

    这里我们介绍一个定期支付合约 允许用户定期存入款项 并且收款者可以定期收取款项 合约实现 合约代码如下 contract Recurring Ripemd160 userPubKeyHash Address of the owner of
  • 【uni-app框架】Vue框架的生命周期之data属性、props属性、及其生命周期函数的执行顺序总结【兼容uni-app总结】

    第一 props参数是实时更新的 而created和data仅会执行一次 当每次重新跳转到当前页面的时候 这也是为什么叫做当前页面或当前组件声明周期 第二 在uni app框架中 APP端和H5端的一些区别 APP端不兼容的特性 H5是最权
  • echarts 重新渲染(重新绘制,重新加载数据)等

    转载于 https www cnblogs com Skrillex p 7904797 html
  • SVN版本控制与分支设置

    使用SVN Eclipse做软件版本控制 http cnshell blog sohu com 157502394 html
  • uvm_info信息定制

    1 uvm自带的打印信息国语繁重 不利于debug uvm info TESTCASE sformatf my case0 new UVM DEBUG UVM INFO home zl Desktop uvm study template
  • 算法分析与设计-分治算法

    在计算机科学中 分治法是一种很重要的算法 字面上的解释是 分而治之 就是把一个复杂的问题分成两个或更多的相同或相似的子问题 再把子问题分成更小的子问题 直到最后子问题可以简单的直接求解 原问题的解即子问题的解的合并 这个技巧是很多高效算法的
  • DSA数字签名算法及其实现

    一 实验目的 在掌握了ElGamal和Schorr数字签名算法的基础上 进一步地学习和掌握DSA签名算法 深入地理解该算法是如何降低了签名信息的长度 当其中一个重要参数选为512bit的素数时 ElGamal签名的长度为1024bit 而D
  • 【react】props的简写方式

    props的简写 用static 就可以把类的属性写在类的里面 div div div div
  • linux(centos)无中文输入,如何解决

    1 终端执行安装命令 yum install Chinese Support 2 如下图 多出Input method 3 点击进行配置 4 reboot重启系统 新建一个文本 试一下输入 问题出来 难道是当时新建虚拟机时没有选择增强型虚拟
  • 【Linux】僵尸进程的检测,清理和避免

    一 僵尸进程的产生 一个进程终止的方法很多 进程终止后有些信息对于父进程和内核还是很有用的 例如进程的ID号 进程的退出状态 进程运行的CPU时间等 因此进程在终止时 回收所有内核分配给它的内存 关闭它打开的所有文件等等 但是还会保留以上极
  • 九章算法

    两个排序的数组A和B分别含有m和n个数 找到两个排序数组的中位数 要求时间复杂度应为O log m n 在线评测地址 LintCode 领扣 说明 中位数的定义 这里的中位数等同于数学定义里的中位数 中位数是排序后数组的中间值 如果有数组中