【每日一题Day72】LC855考场就座 | 构造数据结构 动态数组+二分查找

简介: 可以使用哈希表记录每个座位是否有人入座,但是当n很大时,每次在搜索距离的时候,不可避免的会搜索到未就座的座位,时间复杂度为O(n);因此我使用动态数组isSeated记录已经就座的座位序号,并手动保证数组为升序排列(也可以使用TreeSet 做的时候没想到…),然后遍历数组,按照规律就座以及移除座位序号。

考场就座【LC855】


There is an exam room with n seats in a single row labeled from 0 to n - 1.


When a student enters the room, they must sit in the seat that maximizes the distance to the closest person. If there are multiple such seats, they sit in the seat with the lowest number. If no one is in the room, then the student sits at seat number 0.


Design a class that simulates the mentioned exam room.


Implement the ExamRoom class:


  • ExamRoom(int n) Initializes the object of the exam room with the number of the seats n.
  • int seat() Returns the label of the seat at which the next student will set.
  • void leave(int p) Indicates that the student sitting at seat p will leave the room. It is guaranteed that there will be a student sitting at seat p.


复杂度还可以,至少比官方题解好,希望没分析错


  • 思路:


2022/12/30


。由于每次就座时,有三种情况:


1.无人就座:坐在0号座位


2.1人就座:坐在0号或者n-1号座位


3.2人及以上就座:就座座位与相邻两个座位的最小距离最大,因此可以在已就座的座位中找到相邻两个座位距离一半的最大值dis,并记录前一个座位在序号preSeat,新座位序号即为preSeat+dis。


。可以使用哈希表记录每个座位是否有人入座,但是当n很大时,每次在搜索距离的时候,不可避免的会搜索到未就座的座位,时间复杂度为O(n);因此我使用动态数组isSeated记录已经就座的座位序号,并手动保证数组为升序排列(也可以使用TreeSet 做的时候没想到…),然后遍历数组,按照规律就座以及移除座位序号。


  • 实现


。初始化:记录位置数量n


。seat


1.情况1:isSeated大小为0,在0号座位就座


2.合并情况2和情况3:


每次就座时在preSeat的后方插入新座位,因此记录preSeat在数组中的下标preIndex,在其后方插入新座位,保证数组为升序排列


  • 首先在已就座的座位中找到相邻两个座位距离一半的最大值dis,并记录preIndex


  • 然后处理0号座位未就座的情况,此时的距离为isSeated.get(0),若isSeated.get(0)>dis,那么更新dis以及preIndex


  • 再处理n-1号座位未就座的情况,此时的距离为n- 1 - isSeated.get(size - 1),若n- 1 - isSeated.get(size - 1)>dis,那么更新dis以及preIndex


  • 最后将该位置添加至数组中的相应位置

(也可以在数组中添加哑结点,就不用额外处理了)


。leave:二分查找座位p的下标,通过remove删除


class ExamRoom {
    int n;
    // boolean[] isSeated;
    List<Integer> isSeated;
    public ExamRoom(int n) {
        // isSeated = new boolean[n];
        isSeated = new ArrayList<>();
        this.n = n;
    }
    public int seat() {
        int size = isSeated.size();
        if (size == 0){
            isSeated.add(0);
            return 0;
        }
        // else if (size == 1){
        //     int temp = isSeated.get(0);
        //     if (isSeated.get(0) < n / 2){
        //         isSeated.add(1, n - 1);
        //         return n - 1;
        //     }else{
        //         isSeated.add(0, 0);
        //         return 0;
        //     }
        // }
        int dis = 0;
        int preIndex = -1;
        // 首位
        if (isSeated.get(0) != 0 ){
            dis = isSeated.get(0);
        }
        for (int i = 0; i < size - 1; i++){
            int temp = (isSeated.get(i + 1) - isSeated.get(i)) / 2;
            if (dis < temp){
                dis = temp;
                preIndex = i;
            }
        }
        // 末尾
        if (n - 1 - isSeated.get(size - 1) > dis){
            dis = n - 1 - isSeated.get(size - 1);
            preIndex = size - 1;
        }
        int ans = preIndex == -1 ? 0 : isSeated.get(preIndex) + dis;
        isSeated.add(preIndex + 1, ans);
        return ans;
    }
    public void leave(int p) {
        int l = 0, r = isSeated.size() - 1;
        while (l <= r){
            int mid = (l + r) >> 1;
            if (isSeated.get(mid) == p){
                isSeated.remove(mid);
                return;
            }else if (isSeated.get(mid) > p){
                r = mid - 1;
            }else{
                l = mid + 1;
            }
        }
    }
}


。复杂度


  • 时间复杂度:seat函数的时间复杂度为O(P),每次都需要遍历整个动态数组,每次add添加元素的时间复杂度为O ( P ) 。leave函数的时间复杂度为O(P+logP),二分查找的时间复杂度为O(logP),remove时间复杂度为O(P)。


  • 空间复杂度:O ( P )


1cddc62a4198a90f8c5b5a86a288024c.png


  • TreeSet


class ExamRoom {
    int N;
    TreeSet<Integer> students;
    public ExamRoom(int N) {
        this.N = N;
        students = new TreeSet();
    }
    public int seat() {
        //Let's determine student, the position of the next
        //student to sit down.
        int student = 0;
        if (students.size() > 0) {
            //Tenatively, dist is the distance to the closest student,
            //which is achieved by sitting in the position 'student'.
            //We start by considering the left-most seat.
            int dist = students.first();
            Integer prev = null;
            for (Integer s: students) {
                if (prev != null) {
                    //For each pair of adjacent students in positions (prev, s),
                    //d is the distance to the closest student;
                    //achieved at position prev + d.
                    int d = (s - prev) / 2;
                    if (d > dist) {
                        dist = d;
                        student = prev + d;
                    }
                }
                prev = s;
            }
            //Considering the right-most seat.
            if (N - 1 - students.last() > dist)
                student = N - 1;
        }
        //Add the student to our sorted TreeSet of positions.
        students.add(student);
        return student;
    }
    public void leave(int p) {
        students.remove(p);
    }
}
作者:力扣 (LeetCode)
链接:https://leetcode.cn/problems/exam-room/solutions/20105/kao-chang-jiu-zuo-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


。复杂度


  • 时间复杂度:seat函数的时间复杂度为O(P+logP),每次都需要遍历整个动态数组,每次add添加元素的时间复杂度为O(logP)。leave函数的时间复杂度为O(logP),remove时间复杂度为O(logP)。


  • 空间复杂度:O ( P )


e70813e384c68b90dbed9cc8e62edd8d.png

目录
相关文章
|
18天前
|
存储 Java 程序员
数据结构之 - 深入了解数组数据结构
数据结构之 - 深入了解数组数据结构
34 6
|
18天前
|
存储 算法 搜索推荐
探索常见数据结构:数组、链表、栈、队列、树和图
探索常见数据结构:数组、链表、栈、队列、树和图
84 64
|
11天前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
16 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
11天前
|
存储 算法 定位技术
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
15 0
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
|
11天前
|
算法 Java 索引
数据结构与算法学习十五:常用查找算法介绍,线性排序、二分查找(折半查找)算法、差值查找算法、斐波那契(黄金分割法)查找算法
四种常用的查找算法:顺序查找、二分查找(折半查找)、插值查找和斐波那契查找,并提供了Java语言的实现代码和测试结果。
13 0
|
1月前
|
存储 Java
java数据结构,线性表顺序存储(数组)的实现
文章介绍了Java中线性表顺序存储(数组)的实现。线性表是数据结构的一种,它使用数组来实现。文章详细描述了线性表的基本操作,如增加、查找、删除、修改元素,以及其他操作如遍历、清空、求长度等。同时,提供了完整的Java代码实现,包括MyList接口和MyLinearList实现类。通过main函数的测试代码,展示了如何使用这些方法操作线性表。
|
3月前
|
存储
【数据结构OJ题】轮转数组
力扣题目——轮转数组
39 2
【数据结构OJ题】轮转数组
|
2月前
|
存储 Java 程序员
"揭秘HashMap底层实现:从数组到链表,再到红黑树,掌握高效数据结构的秘密武器!"
【8月更文挑战第21天】HashMap是Java中重要的数据结构,采用数组+链表/红黑树实现,确保高效查询与更新。构造方法初始化数组,默认容量16,负载因子0.75触发扩容。`put`操作通过计算`hashCode`定位元素,利用链表或红黑树处理冲突。`get`和`remove`操作类似地定位并返回或移除元素。JDK 1.8优化了链表转红黑树机制,提升性能。理解这些原理能帮助我们更高效地应用HashMap。
40 0
|
4月前
|
存储 算法 调度
【数据结构与算法】详解循环队列:基于数组实现高效存储与访问
【数据结构与算法】详解循环队列:基于数组实现高效存储与访问
|
4月前
|
存储 JavaScript 前端开发
JavaScript中的数组是核心数据结构,用于存储和操作序列数据
【6月更文挑战第22天】JavaScript中的数组是核心数据结构,用于存储和操作序列数据。创建数组可以使用字面量`[]`或`new Array()`。访问元素通过索引,如`myArray[0]`,修改同样如此。常见方法包括:`push()`添加元素至末尾,`pop()`移除末尾元素,`shift()`移除首元素,`unshift()`添加到开头,`join()`连接为字符串,`slice()`提取子数组,`splice()`进行删除、替换,`indexOf()`查找元素位置,`sort()`排序数组。还有其他如`reverse()`、`concat()`等方法。
128 2