数据结构与算法——Java实现 36.求数据流中位数

news2024/10/21 17:23:24

就借着月光,再与你对望

                                        —— 24.10.14

295. 数据流的中位数

中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值

  • 例如 arr = [2,3,4] 的中位数是 3 。
  • 例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5 。

实现 MedianFinder 类:

  • MedianFinder() 初始化 MedianFinder 对象。

  • void addNum(int num) 将数据流中的整数 num 添加到数据结构中。

  • double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

输入
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
输出
[null, null, null, 1.5, null, 2.0]

解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1);    // arr = [1]
medianFinder.addNum(2);    // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3);    // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

思路

由于寻找的是中位数,我们可以把所有的数据分成两份,由于是数据流的中位数,中间的数不断地变化,但是我们可以把数据流分成较大的数和较小的数两部分,找出较小数中最大的和较大数中最小的,用一个大顶堆和一个小顶堆实现,注意两个堆中元素个数最多相差一个,元素多的那个堆的堆顶元素就是我们查找的中位数,然后数据流中添加元素,在两个堆中继续进行判断,找到中位数

为了保证两边数据量的平衡

        两边个数一样时,左边个数加一

        两边个数不一样时,右边个数加一

但是,随便一个数能直接加入吗?

        左边个数加一时,应该加入右边堆,跟右边堆中所有元素进行比较,挑右边堆中最小的元素取走加入到左边堆

        右边个数加一时,应该加入左边堆,挑左边堆中所有元素进行比较,挑左边堆中最大的元素取走加入到右边堆

Heap类实现

import java.util.Arrays;

public class Heap {
    int[] array;
    int size;
    boolean max;

    public int size() {
        return size;
    }

    public Heap(int capacity, boolean max) {
        this.array = new int[capacity];
        this.max = max;
    }

    public Heap(int[] array, boolean max) {
        this.array = array;
        this.size = array.length;
        this.max = max;
        heapify();
    }

    /**
     * 获取堆顶元素
     *
     * @return 堆顶元素
     */
    public int peek() {
        return array[0];
    }

    /**
     * 删除堆顶元素
     *
     * @return 堆顶元素
     */
    public int poll() {
        int top = array[0];
        swap(0, size - 1);
        size--;
        down(0);
        return top;
    }

    /**
     * 删除指定索引处元素
     *
     * @param index 索引
     * @return 被删除元素
     */
    public int poll(int index) {
        int deleted = array[index];
        up(Integer.MAX_VALUE, index);
        poll();
        return deleted;
    }

    /**
     * 替换堆顶元素
     *
     * @param replaced 新元素
     */
    public void replace(int replaced) {
        array[0] = replaced;
        down(0);
    }

    /**
     * 堆的尾部添加元素
     *
     * @param offered 新元素
     */
    public void offer(int offered) {
        if (size == array.length) {
            // 扩容
            grow();
        }
        up(offered, size);
        size++;
    }

    // 堆扩容
    private void grow() {
        // 长度增加为原先的1.5倍
        int capacity = (int)(size * 1.5);
        // int capacity = size + (size >> 1);
        int[] newArray = new int[capacity];
        System.arraycopy(array, 0,
                newArray, 0, size);
        array = newArray;
    }

    // 将 offered 元素上浮: 直至 offered 小于父元素或到堆顶
    private void up(int offered, int index) {
        int child = index;
        while (child > 0) {
            int parent = (child - 1) / 2;
            boolean cmp = max ? offered > array[parent] : offered < array[parent];
            if (cmp) {
                array[child] = array[parent];
            } else {
                break;
            }
            child = parent;
        }
        array[child] = offered;
    }

    // 建堆
    private void heapify() {
        // 如何找到最后这个非叶子节点  size / 2 - 1
        for (int i = size / 2 - 1; i >= 0; i--) {
            down(i);
        }
    }

    // 将 parent 索引处的元素下潜: 与两个孩子较大者交换, 直至没孩子或孩子没它大
    private void down(int parent) {
        int left = parent * 2 + 1;
        int right = left + 1;
        int maxOrMin = parent;
        if (left < size && (max ? array[left] > array[maxOrMin] : array[left] < array[maxOrMin])) {
            maxOrMin = left;
        }
        if (right < size && (max ? array[right] > array[maxOrMin] : array[right] < array[maxOrMin])) {
            maxOrMin = right;
        }
        if (maxOrMin != parent) { // 找到了更大的孩子
            swap(maxOrMin, parent);
            down(maxOrMin);
        }
    }

    // 交换两个索引处的元素
    private void swap(int i, int j) {
        int t = array[i];
        array[i] = array[j];
        array[j] = t;
    }

    /*
              100
           /      \
          10      99
         / \      / \
        5   6    98 97
       /\   /\   /
      1 2  3  4 96

              100
           /      \
          96      99
         / \      / \
        10   6   98 97
       /\   /\
      1 2  3  4
     */
    public static void main(String[] args) {
        Heap heap = new Heap(5, true); //100,10,99,5,6,98,97,1,2,3,4,96
        heap.offer(100);
        heap.offer(10);
        heap.offer(99);
        heap.offer(5);
        heap.offer(6);
        heap.offer(98);
        heap.offer(97);
        heap.offer(1);
        heap.offer(2);
        heap.offer(3);
        heap.offer(4);
        heap.offer(96);
        System.out.println(Arrays.toString(heap.array));
        System.out.println(heap.size);
        System.out.println(heap.poll(3));
        System.out.println(Arrays.toString(heap.array));
        System.out.println(heap.size);
    }
}

求数据流中位数

import java.util.Arrays;

public class LeetCode295MiddleInDataStream {
    @Override
    public String toString() {
        int size = left.size;
        int[] left = new int[size];
        for (int i = 0; i < left.length; i++) {
            left[size - 1 - i] = this.left.array[i];
        }
        int[] right = Arrays.copyOf(this.right.array, this.right.size);
        return Arrays.toString(left) + " <-> " + Arrays.toString(right);
    }

    public void addNum(int num) {
        if (left.size() == right.size()){
            right.offer(num);
            left.offer(right.poll());
        }else {
            left.offer(num);
            right.offer(left.poll());
        }
    }

    private Heap left = new Heap(10,true);
    private Heap right = new Heap(10,false);

    // 两边数据一致,左右各取堆顶元素求平均左边多一个,取左边堆顶元素
    public double findMedian(){
        if (left.size() == right.size()){
            return (left.peek() + right.peek()) / 2.0;
        }else
            return left.peek();
    }

    public static void main(String[] args) {
        LeetCode295MiddleInDataStream obj = new LeetCode295MiddleInDataStream();
        obj.addNum(10);
        obj.addNum(5);
        obj.addNum(3);
        obj.addNum(4);
        obj.addNum(6);
        obj.addNum(7);
        obj.addNum(8);
        obj.addNum(9);
        System.out.println(obj);
        obj.addNum(10);
        System.out.println(obj);
        System.out.println(obj.findMedian());
    }
}

力扣提交

import java.util.Arrays;

public class Heap {
    int[] array;
    int size;
    boolean max;

    public int size() {
        return size;
    }

    public Heap(int capacity, boolean max) {
        this.array = new int[capacity];
        this.max = max;
    }

    /**
     * 获取堆顶元素
     *
     * @return 堆顶元素
     */
    public int peek() {
        return array[0];
    }

    /**
     * 删除堆顶元素
     *
     * @return 堆顶元素
     */
    public int poll() {
        int top = array[0];
        swap(0, size - 1);
        size--;
        down(0);
        return top;
    }

    /**
     * 删除指定索引处元素
     *
     * @param index 索引
     * @return 被删除元素
     */
    public int poll(int index) {
        int deleted = array[index];
        up(Integer.MAX_VALUE, index);
        poll();
        return deleted;
    }

    /**
     * 替换堆顶元素
     *
     * @param replaced 新元素
     */
    public void replace(int replaced) {
        array[0] = replaced;
        down(0);
    }

    /**
     * 堆的尾部添加元素
     *
     * @param offered 新元素
     */
    public void offer(int offered) {
        if (size == array.length) {
            // 扩容
            grow();
        }
        up(offered, size);
        size++;
    }

    private void grow() {
        int capacity = size + (size >> 1);
        int[] newArray = new int[capacity];
        System.arraycopy(array, 0,
                newArray, 0, size);
        array = newArray;
    }

    // 将 offered 元素上浮: 直至 offered 小于父元素或到堆顶
    private void up(int offered, int index) {
        int child = index;
        while (child > 0) {
            int parent = (child - 1) / 2;
            boolean cmp = max ? offered > array[parent] : offered < array[parent];
            if (cmp) {
                array[child] = array[parent];
            } else {
                break;
            }
            child = parent;
        }
        array[child] = offered;
    }

    public Heap(int[] array, boolean max) {
        this.array = array;
        this.size = array.length;
        this.max = max;
        heapify();
    }

    // 建堆
    private void heapify() {
        // 如何找到最后这个非叶子节点  size / 2 - 1
        for (int i = size / 2 - 1; i >= 0; i--) {
            down(i);
        }
    }

    // 将 parent 索引处的元素下潜: 与两个孩子较大者交换, 直至没孩子或孩子没它大
    private void down(int parent) {
        int left = parent * 2 + 1;
        int right = left + 1;
        int maxOrMin = parent;
        if (left < size && (max ? array[left] > array[maxOrMin] : array[left] < array[maxOrMin])) {
            maxOrMin = left;
        }
        if (right < size && (max ? array[right] > array[maxOrMin] : array[right] < array[maxOrMin])) {
            maxOrMin = right;
        }
        if (maxOrMin != parent) { // 找到了更大的孩子
            swap(maxOrMin, parent);
            down(maxOrMin);
        }
    }

    // 交换两个索引处的元素
    private void swap(int i, int j) {
        int t = array[i];
        array[i] = array[j];
        array[j] = t;
    }
}


class MedianFinder {
    Heap left = new Heap(10,true);
    Heap right = new Heap(10,false);
    
    public void addNum(int num) {
        if (left.size == right.size){
            right.offer(num);
            left.offer(right.poll());
        }else {
            left.offer(num);
            right.offer(left.poll());
        }
    }
    
    public double findMedian() {
        if (left.size() == right.size()){
            return (left.peek() + right.peek()) / 2.0;
        }else
            return left.peek();
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2213811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java jconsole.exe 调试工具

寻找JDK所在路径&#xff1a; 打开 jconsole.exe 启动之前确保idea程序已经运行了有的同学需要用管理员方式运行&#xff0c;即 右键 jconsole.exe 选择用管理员方式运行 上面的折线图&#xff0c;表示随着时间有哪些线程 下面的左边的线程中&#xff1a;Thread tnew MyThre…

【Java数据结构】链表面试题

【Java数据结构】链表面试题 一、移除链表元素二、反转链表三、链表的中间结点四、删除链表的倒数第 N 个结点五、合并两个有序链表六、链表分割七、链表的回文结构八、相交链表九、环形链表 此篇博客希望对你有所帮助&#xff08;帮助你更加了解链表&#xff09;&#xff0c;不…

stm32 为什么有2个晶振?8mhz+32.768k

1个是系统时钟晶振是单片机内部系统的主时钟源&#xff0c;它负责控制整个系统的时钟频率。这个晶振的频率一般比较高&#xff0c;通常在几十MHz到几百MHz不等。它和CPU以及各种总线之间相互配合&#xff0c;从而协同工作。 另外一个是外设时钟晶振则通常用于单片机的内部外设…

pnpm报错 cannot find package xxx,有的电脑正常运行,只有这个的电脑报错

pnpm build报错 cannot find package xxx&#xff0c;有的电脑正常运行&#xff0c;只有这一个报错 在网上查找各种资料发现是项目在电脑里的目录层级比较深导致的。 问题&#xff1a;在 Windows 系统上&#xff0c;文件路径过长&#xff08;超过 260 个字符&#xff09;可能…

2024年10月14日读书笔记第三篇-

我们继续学习 自下而上思考&#xff0c;总结概括 如果你将所有的信息进行归类分组、抽象概括&#xff0c;并以自上而下的方式表达出来&#xff0c;那么你的文章结构会如图1-3所示。每个方框代表你希望表达的一个思想。 你的思维从最底部的层次开始构建&#xff0c;将句子按照某…

CUDA 事件计时

CUDA 事件 可以为主机代码和设备代码计时。 基本的语法 // 定义事件变量 cudaEvent_t start, stop; // 初始化 cudaEventCreate(&start); cudaEventCreate(&stop); // 记录代表时间开始的事件&#xff0c;注意不是地址 cudaEventRecord(start); // 在TCC的驱动下可以…

【网络安全】1,600$:Auth0 错误配置

未经许可,不得转载。 文章目录 前言正文漏洞案例修复建议前言 Auth0 是一个广泛用于网站和应用程序的身份验证平台,负责管理用户身份并确保其服务的安全访问。该平台提供了多种工作流程,以无缝集成登录和注册流程。 在 Auth0 中创建新应用时,注册选项默认启用。当系统禁用…

Go 1.19.4 命令调用、日志、包管理、反射-Day 17

1. 系统命令调用 所谓的命令调用&#xff0c;就是通过os&#xff0c;找到系统中编译好的可执行文件&#xff0c;然后加载到内存中&#xff0c;变成进程。 1.1 exec.LookPath&#xff08;寻找命令&#xff09; 作用&#xff1a; exec.LookPath 函数用于在系统的环境变量中搜索可…

【每日刷题】Day139

51. N 皇后 - 力扣&#xff08;LeetCode&#xff09; //思路&#xff1a;DFS哈希。 //本题的难点就在于剪枝操作。 class Solution { public: int n; vector<vector<string>> ans; vector<string> path; bool cols[10], dig1[20], dig2[20]; void dfs(int ro…

【无标题】基于情境依赖因果影响的多智能体协作强化学习

、文章探讨了大型语言模型&#xff08;LLMs&#xff09;&#xff0c;例如GPT-4&#xff0c;是否以及在何种意义上拥有知识。作者认为&#xff0c;这些模型展现了一种称为“工具性知识”的能力&#xff0c;这种知识允许它们根据输入上下文推断任务结构&#xff0c;并在此基础上进…

appium启动hbuild打包的apk异常解决

目录 一、错误信息 二、问题解决 2.1 通过以下命令获取安装包名称&#xff1a; 2.2 这个launcher状态下的安装包名称和active&#xff0c;替换原先的安装包名称 一、错误信息 通过adb shell dumpsys activity | findstr "mResume" 命令获取的安装包信息&#xff…

2024年公路水运工程施工企业主要负责人证模拟考试题库及公路水运工程施工企业主要负责人理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年公路水运工程施工企业主要负责人证模拟考试题库及公路水运工程施工企业主要负责人理论考试试题是由安全生产模拟考试一点通提供&#xff0c;公路水运工程施工企业主要负责人证模拟考试题库是根据公路水运工程施…

芯课堂 | 使用 SWM341 系列 MCU 环境配置

SWM341系列MCU调试环境配置 SWM341 是华芯微特的其中一款 MCU&#xff0c;341 和 341内置 SDRAM 的 34S 系列&#xff0c;其内核相同。 芯片使用安谋科技“星辰”STAR-MC1 内核,基于 Arm-V8 指令集架构&#xff0c;下载烧录选 M33&#xff0c;对应的工具需要升级; 1、使用 KE…

MySQL中库的操作

目录 前言 一、查看数据库 1.1、语法 二、创建数据库 2.1、语法 2.2、自定义⼀个数据库 2.2.1、创建一个名为“aokey”的数据库 三、字符集编码和校验(排序)规则 3.1、查看数据库支持的字符集编码 3.2、查看数据库支持的排序规则 3.3、不同的字串集与排序规则对数…

芯片供电引脚为什么要放一个104电容?

每个芯片的电源都要接一个104电容是为什么? 这颗电容叫高频旁路电容&#xff0c;作用就是把电源中的高频杂波对地短路防止高频污染&#xff0c;降低电源输入对芯片的影响。 接地滤波电容&#xff0c;如果没有&#xff0c;杂波干扰非常大&#xff0c;有了这个滤波电容&#x…

Json-Rpc框架(项目设计 —— 服务端客户端 模块功能划分简介)

阅读导航 引言一、理解项目功能⭕分布式RPC服务发现/注册/订阅/创建 结构图⭕项目三大核心功能 二、服务端模块划分简介三、客户端模块划分简介 引言 在上一篇文章中&#xff0c;我们深入探讨了Json-Rpc框架中不可或缺的序列化与反序列化利器——JsonCpp库。 在本篇文章中&am…

Python酷玩之旅_如何正确使用pipreqs完成依赖导出(解决UnicodeDecodeError)

导览 前言Q&#xff1a;如何正确使用pipreqs1. 安装库2. 命令详解2.1 options2.2 path 3. 实践与问题 结语系列回顾 前言 使用python开发的应用在迁移部署的时候&#xff0c;你一定会使用pipreqs&#xff08;当然也有其他选择&#xff09;吧&#xff1f;这是一款强大的依赖管理…

【STM32CubeMX开发】-2.1-TIM_生成一个定时器中断

目录 1 Tim定时器的时钟源 2 Tim定时器的配置 2.1 中断配置 3 生成代码 4 测试结果 结尾 1 Tim定时器的时钟源 TIM1的时钟来源自APB1 Timer clocks&#xff0c;时钟树上所有总线频率均设置为了STM32F0能达到的最高频率&#xff0c;此时APB1 Timer clocks 48MHz。 2 Tim…

pdf加密怎么弄?8款热门pdf加密工具大盘点,速来收藏!(2024)

在如今&#xff0c;分享pdf文件时确保敏感信息的安全显得尤为重要。如果没有适当的保护措施&#xff0c;这些文件可能会被不法人士访问&#xff0c;从而对您的数据和声誉造成风险。为了避免这种潜在的问题&#xff0c;在Windows操作系统上加密pdf文件就显得非常有必要。如果您不…

vue3 使用 Vue Router实现前端路由控制

vue3 使用 Vue Router实现前端路由控制 **在现代Web应用中&#xff0c;前端路由控制是非常重要的一部分。它可以帮助我们将不同的页面内容展示给用户&#xff0c;同时保持用户在浏览不同页面时的连贯性。本文将介绍如何使用Vue Router来实现前端路由控制。 首先&#xff0c;我…