Leetcode 855. 考场就座

news2024/11/6 7:13:41
在考场里,一排有 N 个座位,分别编号为 0, 1, 2, ..., N-1 。

当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上。如果有多个这样的座位,他会坐在编号最小的座位上。(另外,如果考场里没有人,那么学生就坐在 0 号座位上。)

返回 ExamRoom(int N) 类,它有两个公开的函数:其中,函数 ExamRoom.seat() 会返回一个 int (整型数据),代表学生坐的位置;函数 ExamRoom.leave(int p) 代表坐在座位 p 上的学生现在离开了考场。每次调用 ExamRoom.leave(p) 时都保证有学生坐在座位 p 上。

 

示例:

输入:["ExamRoom","seat","seat","seat","seat","leave","seat"], [[10],[],[],[],[],[4],[]]
输出:[null,0,9,4,2,null,5]
解释:
ExamRoom(10) -> null
seat() -> 0,没有人在考场里,那么学生坐在 0 号座位上。
seat() -> 9,学生最后坐在 9 号座位上。
seat() -> 4,学生最后坐在 4 号座位上。
seat() -> 2,学生最后坐在 2 号座位上。
leave(4) -> null
seat() -> 5,学生最后坐在 5 号座位上。

 

提示:

    1 <= N <= 10^9
    在所有的测试样例中 ExamRoom.seat() 和 ExamRoom.leave() 最多被调用 10^4 次。
    保证在调用 ExamRoom.leave(p) 时有学生正坐在座位 p 上。


解法一:有序集合

首先,对于给定的区间 [ L , R ] [L, R] [L,R],即 L , R L,R L,R的座位已经选择,当我们要在这个区间进行放置的时候,为了使离他最近的人之间的距离达到最大化,我们应该将座位选择在中点 ( L + R ) / 2 (L+R)/2 (L+R)/2,所能够得到的距离定义为 ( R − L ) / 2 (R-L)/2 (RL)/2

例如: 
[1, 4]
我们应该选择在2点,距离最近的人距离为1, 即1 2 . 4
[1, 5]
我们应该选择在3点,距离最近的人距离为2,即1.3.5

Seat():


接下来,我们创建一个有序集合来保存已经选了的座位,最开始当集合为空时,必然选择下标为0的座位。若集合不为空,则遍历集合中所有的区间,选择出一个距离最大的中点出来,最后再特判一下选择0点和n-1点的情况
在这里插入图片描述

对于0点和n-1点来说:
0点的距离为:集合中第一个元素-0, 即set.first()
n-1点的距离为:(n-1) - 集合中最后一个元素,即n-1-set.last()

Leave():


对于删除操作,我们直接在有序集合中将要删除的座位移除即可。

  • 时间复杂度:seat(): O ( n ) O(n) O(n), leave(): O ( l o g n ) O(logn) O(logn)
  • 空间复杂度: O ( n ) O(n) O(n)
class ExamRoom {
    TreeSet<Integer> set;
    int n;
    public ExamRoom(int n) {
        this.n = n;
        set = new TreeSet<>(); 
    }
    public int seat() {
        if (set.size() == 0) {set.add(0); return 0;} //没有人时,一定返回0
        int pre = set.first(), ans = set.first(), idx = 0; //初始话为选择最左的长度
        for (int x : set) {
            if (ans < (x - pre) / 2) {
                ans = (x - pre) / 2;
                idx = (x + pre) / 2;
            }
            pre = x;
        }
        //最右进行判断
        int d = n - 1 - set.last();
        if (ans < d) {ans = d; idx = n - 1;}
        set.add(idx);
        return idx;
    }

    public void leave(int p) { 
        set.remove(p);
    }
}

解法二:有序集合 + 优先队列

Seat():


在解法一的基础上,我们可以通过优先队列来优化我们区间选择的操作,在解法一我们通过遍历来求的拥有最大距离的区间,再特判一下最左最右端点。我们可以将区间直接放在优先队列中,通过优先队列每次弹出距离最大的一个区间,再选择区间的中点即可


我们每次从优先队列中弹出一个区间 [ L , R ] [L,R] [L,R],这是所有区间中距离最大的一个区间,我们将它与选择 0 0 0点和选择 n − 1 n-1 n1点的距离进行比较,若更大,那么我们就选择中点为 m i d = ( L + R ) / 2 mid=(L+R)/2 mid=(L+R)/2,会产生新的区间 [ L , m i d ] [L,mid] [L,mid] [ m i d , R ] [mid, R] [mid,R],放入优先队列中;若选择最左最右点,那么也会产生新的区间 [ 0 , s e t . f i r s t ( ) ] [0,set.first()] [0,set.first()] [ s e t . l a s t ( ) , n − 1 ] [set.last(), n - 1] [set.last(),n1]

Leave():


对于删除操作,当我们删除集合中第一个元素和最后一个元素时,并不会产生新的区间。当删除集合中间的元素时,如 [ L , m i d , R ] [L,mid,R] [L,mid,R],我们删除mid点会产生新的区间 [ L , R ] [L, R] [L,R]将新区间添加进我们的优先队列中即可。


而我们还需要在队列中将以前的区间 [ L , m i d ] 和 [ m i d , R ] [L,mid]和[mid, R] [L,mid][mid,R]进行删除,我们使用延迟删除的技巧,因此在 s e a t ( ) seat() seat()中我们从队列中弹出的区间可能是需要删除的,我们判断区间的两个端点是否在集合set中,若不在代表需要删除,并且区间中间不能有任何座位被选取

  • 时间复杂度:seat(): O ( l o g n ) O(logn) O(logn), leave(): O ( l o g n ) O(logn) O(logn)
  • 空间复杂度: O ( n ) O(n) O(n)
class ExamRoom {
    PriorityQueue<int[]> q;
    TreeSet<Integer> set;
    int n;
    public ExamRoom(int n) {
        this.n = n;
        q = new PriorityQueue<>((a, b) -> {
            int d1 = (a[1] - a[0]) / 2, d2 = (b[1] - b[0]) / 2;
            return d1 == d2 ? a[0] - b[0] : d2 - d1; //当长度相等时,坐标更小先弹出,当不相等时,长度更大的先弹出
        });
        set = new TreeSet<>(); //创建有序集合
    }

    public int seat() {
        if (set.size() == 0) { set.add(0); return 0;} //1.没有人时,一定返回0
        int d1 = set.first(), d2 = n - 1 - set.last(); //获取最左和最右放置学生能获取的长度
        while (set.size() >= 2) { //2.大于等于两个人的时候,可以选择最左最右 或者中间的区间
            int[] t = q.poll();
            if (!set.contains(t[0]) || !set.contains(t[1]) || set.higher(t[0]) != t[1]) continue; //无效区间,某个端点已经被删除
            int d3 = (t[1] - t[0]) / 2;                
            if (d3 <= d1 || d3 < d2) {q.add(new int[]{t[0], t[1]}); break;}; //选择最左或者最右
            int mid = (t[0] + t[1]) / 2; //选择终点
            q.add(new int[]{t[0], mid});
            q.add(new int[]{mid, t[1]});
            set.add(mid);  
            return mid;
        } 
        //3.选择最左或者最右的位置
        int l = 0, r = set.first(), sel = 0;
        if (d1 < d2) {l = set.last(); r = n - 1; sel = n - 1;}
        q.add(new int[]{l, r});
        set.add(sel);
        return sel;
    }

    public void leave(int p) { 
        if (p != set.first() && p != set.last()) q.add(new int[]{set.lower(p), set.higher(p)}); //如果不是删除两端点, 那么会增加新区间
        set.remove(p); 
    }
}

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

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

相关文章

「回顾2022,展望2023」- 技术和兴趣,工作和生活,我们都在旅途中

技术 Java&#xff0c;云原生&#xff0c;前端 技术面好像变窄了 远离CI/CD&#xff0c;重新回到起点 2019年毕业&#xff0c;2018年9月份在距离学校(南昌)不远不近的公司实习开始实习&#xff0c;毕业后去过深圳、上海&#xff0c;最后是杭州&#xff0c;到现在&#xff0c;又…

OV7670图像传感器介绍

OV7670图像传感器简介 OV7670是图像传感器&#xff0c;其体积小、工作电压低&#xff0c;能提供单片VGA摄像头和影像处理器的所有功能。通过SCCB 总线控制&#xff0c;可以输出整帧、子采样、取窗口等方式的各种分辨率8位影响数据。该产品VGA图像最高达到30帧/秒。用户可以完全…

西门子PLC串口协议与以太网通信协议对比

西门子plc品牌众多&#xff0c;通信协议的类型就更多了&#xff0c;具体可分为串口协议和以太网通信协议两大类。 串口协议主要有&#xff1a;MODBUS RTU 通信协议&#xff1b;PROFIBUS 通信协议&#xff1b;USS通信协议&#xff1b;PPI通信协议&#xff1b;MPI通信协议&#…

YGG:2022年年终回顾

2022 年&#xff0c;Yield Guild Games&#xff08;YGG&#xff09;扩大并发展了区块链游戏生态系统和开放的元宇宙。在这篇文章中&#xff0c;我们庆祝我们的公会成员、合作伙伴和社区所取得的里程碑式的成就&#xff0c;并期待接下来的发展。 游戏和基础设施合作伙伴的数量不…

推荐 6 个 GitHub 开源项目

本期推荐开源项目目录&#xff1a;1. B 站自动任务工具2. 学习 Solidity3. 高性能异步抖音爬取工具4. Java学习指南5. 中后台管理系统模版6. ChatGPT 中文调教指南01B 站自动任务工具BiliBiliTool 是一个自动化工具&#xff0c;它可以帮助你每天获取经验、每日签到、批量取关等…

分享几个嵌入式 C 中的实用技巧

1、动态绑定、回调函数 回调函数可以达到动态绑定的作用&#xff0c;在一定程度上可以降低层与层之间的耦合。关于回调函数&#xff0c;之前已经有写过一篇&#xff1a;C语言、嵌入式重点知识&#xff1a;回调函数。可能很多初学的小伙伴可能还不理解回调函数&#xff0c;可以…

【计算机图形学入门】笔记9:Shading3着色(插值、高级纹理映射)

09Shading3着色&#xff08;插值、高级纹理映射&#xff09;1.Barycentric Coordinates 重心坐标1.A点自己的重心坐标2.如何求出任意点的重心坐标&#xff1f;2.如何把纹理应用在实际的渲染中&#xff1f;Applying Textures3.Texture Magnification纹理放大。&#xff08;解决纹…

Transform+ASM插桩系列(1)——熟悉Java字节码

前言 为什么要学习Java字节码呢&#xff0c;因为我们学的是插桩字节码技术&#xff0c;这块技术的根底就是字节码&#xff0c;要学会字节码的阅读和字节码的编写&#xff0c;虽然现在很多工具可以帮我们阅读和编写&#xff0c;但最根本的知识还是要理解的。万层楼高从地起&…

CTF之MISC题目-西游记

CTF系列文章 第一篇 CTF之密码学题目-classical && coding 第二篇 CTF之MISC题目-西游记 文章目录CTF系列文章前言一、题目是什么&#xff1f;二、解题步骤1.下载文件&#xff0c;解压2.暴力破解3.解压文件4.处理文本文件5.手动删除多余字符总结前言 CTF中关于MISC&a…

TensorFlow之回归模型-3

1 基本概念 回归模型 线性 线性模型 非线性模型 线性回归 逻辑回归 Log Loss&#xff08;损失函数&#xff09; 分类临界值 2 效率预测 如上所示&#xff0c;使用测试数据集进行评估、用图形显示逻辑回归的预测结果&#xff0c;其中&#xff0c;test_features是测试特…

hnu计网实验一-应用协议与数据包分析实验(使用Wireshark)

前言&#xff1a;这是个比较简单的实验&#xff0c;个人认为最难的不是分析部分&#xff0c;而是能否抓到一个好的包。为了抓到一个好的包我试了很多个网站&#xff0c;终于抓京东时抓到了令我个人最满意的包&#xff0c;因为没有其他各种杂乱的报文&#xff0c;就是那几条必要…

直流信号线性放大器非隔离转换模块0-10V转0-12V大功率负载180mA导轨安装

概述&#xff1a; 导轨安装DIN11 NIPO 系列模拟信号放大器是一种将输入信号放大、转换成按比例输出的直流信号放大器。产品广泛应用在电力、远程监控、仪器仪表、医疗设备、工业自控等需要直流信号测控的行业。此系列产品内部采用稳压电路&#xff0c;通过等比例控制线性放大输…

视频转换IC大全和桥接芯片大全

视频转换大全&#xff08;桥接芯片大全&#xff09; 本人从事多年视频转换ic多年累计大量宝贵资源&#xff0c;可以和大家交流交流&#xff0c;下面就视频转换或桥接芯片做下相关交流&#xff0c;期望可以帮助大家。 视频转换或视频桥接&#xff1a;就是把视频源的信号格式转成…

《MySQL高级篇》数据库建模工具---PowderDesigner的使用教程

文章目录PowerDesigner的使用11.1 开始界面11.2 概念数据模型11.3 物理数据模型11.4 概念模型转为物理模型11.5 物理模型转为概念模型11.6 物理模型导出SQL语句PowerDesigner的使用 PowerDesigner是一款开发人员常用的数据库建模工具&#xff0c;用户利用该软件可以方便地制作…

Python--字典及基本操作

字典也是 Python 提供的一种常用的数据结构&#xff0c;它用于存放具有映射关系的数据。 比如有份成绩表数据&#xff0c;语文&#xff1a;79&#xff0c;数学&#xff1a;80&#xff0c;英语&#xff1a;92&#xff0c;这组数据看上去像两个列表&#xff0c;但这两个列表的元素…

传统目标跟踪——背景分割法

目录 一、背景分割法 二、流程 三、代码 四、总结 一、背景分割法 传统的前景背景分割方法有GrabCut&#xff0c;分水岭算法&#xff0c;当然也包括一些阈值分割的算法。但是这些算法在应用中往往显得鲁棒性较弱&#xff0c;达不到一个好的分割效果。 现代的背景分割算法融入…

便宜的骨传导耳机好用吗?五款便宜的骨传导耳机推荐

市面上的骨传导耳机价格参差不齐&#xff0c;那价位较低的骨传导耳机能不能入手呢&#xff1f;只能说“一分钱&#xff0c;一分货”但是一些大牌骨传导耳机品牌也会出一些性价比较高的骨传导耳机&#xff0c;依靠大牌技术的加持&#xff0c;耳机的使用体验也会更好。可以看看下…

抓住站点能源革命机遇,必须要了解的十大趋势

编辑 | 阿冒 设计 | 沐由每一天&#xff0c;你做得最多的几件事情是什么&#xff1f;喝八回水&#xff0c;吃三次饭&#xff0c;还有空闲的时候起身溜达几圈&#xff0c;以及穿插其间、难以计数的查看手机次数&#xff1f;可以说&#xff0c;我们中的绝大多数每天在手机上花…

告别丑陋判空,一个Optional类就能搞定

认识Optional Opitonal类就是Java提供的为了解决大家平时判断对象是否为空用&#xff0c;通常会用 null!obj 这样的方式存在的判断&#xff0c;从而令人头疼导致空指针异常&#xff0c;同Optional的存在可以让代码更加简单&#xff0c;可读性跟高&#xff0c;代码写起来更高效…

剑指 Offer 16. 数值的整数次方

题目 实现 pow(x, n) &#xff0c;即计算 x 的 n 次幂函数&#xff08;即&#xff0c;xn&#xff09;。不得使用库函数&#xff0c;同时不需要考虑大数问题。 思路 快速幂算法 求 x^n最简单的方法是通过循环将 n 个 x 乘起来&#xff0c;依次求 x1,x2,...,x^n&#xff0c;…