一个处理Range List的面试题解法

news2024/11/24 3:09:40

大纲

  • 题目
  • 解法
    • Range
      • add
      • remove
    • Tools
    • RangeList
      • add
      • remove
  • 代码

最近看到一个比较有意思的面试题。题目不算难,但是想把效率优化做好,也没那么容易。
我们先看下题目

题目

// Task: Implement a class named 'RangeList'
// A pair of integers define a range, for example: [1, 5). This range includes integers: 1, 2, 3, and 4.
// A range list is an aggregate of these ranges: [1, 5), [10, 11), [100, 201)
/**
*
 * NOTE: Feel free to add any extra member variables/functions you like.
*/
class RangeList {
	/**
	*
	* Adds a range to the list
	* @param {Array<number>} range - Array of two integers that specify beginning and
	end of range.
	*/
	add(range) {
		// TODO: implement this
	}
	
	/**
	*
	* Removes a range from the list
	* @param {Array<number>} range - Array of two integers that specify beginning and
	end of range.
	*/
	remove(range) {
		// TODO: implement this
	}
	
	/**
	*
	* Convert the list of ranges in the range list to a string
	* @returns A string representation of the range list
	*/
	toString() {
		// TODO: implement this
	}
}
// Example run
const rl = new RangeList();
rl.toString(); // Should be ""
rl.add([1, 5]);
rl.toString(); // Should be: "[1, 5)"
rl.add([10, 20]);
rl.toString(); // Should be: "[1, 5) [10, 20)"
rl.add([20, 20]);
rl.toString(); // Should be: "[1, 5) [10, 20)"
rl.add([20, 21]);
rl.toString(); // Should be: "[1, 5) [10, 21)"
rl.add([2, 4]);
rl.toString(); // Should be: "[1, 5) [10, 21)"
rl.add([3, 8]);
rl.toString(); // Should be: "[1, 8) [10, 21)"
rl.remove([10, 10]);
rl.toString(); // Should be: "[1, 8) [10, 21)"
rl.remove([10, 11]);
rl.toString(); // Should be: "[1, 8) [11, 21)"
rl.remove([15, 17]);
rl.toString(); // Should be: "[1, 8) [11, 15) [17, 21)"
rl.remove([3, 19]);
rl.toString(); // Should be: "[1, 3) [19, 21)

这题大体的意思是:设计一个RangeList类,它保存了一批左闭右开的区间。它支持add操作,可以新增一个包含区间,但是可能会影响之前的区间,比如之前的区间是:[3,5) [7,9),新增区间[5,7)之后,区间就变成[3,9);它还支持remove操作,可以删除一个区间,也可能影响之前的区间,比如之前的区间是[3,9),删除[5,7)之后,变成[3,5) [7,9)。
在这里插入图片描述

还有一种特殊区间需要考虑,就是左右值相等的区间。比如[5,5)代表的是一个空区间。

解法

Range

首先我们设计一个Range类,它只是单个区间。

add

如果对其进行add操作,即新增一个区间,则要考虑这两个区间是否相交。如果相交,则返回一个重新整合过的区间;如果不相交,则抛出异常。
在这里插入图片描述

    # add the other range to this range.For example, [1, 5) add [5, 7) is [1, 7).
    # @param other - the other range to add to this range
    # @return - the new range after adding
    # @throws TypeError if other is not a Range object or a list of integers
    # @throws ValueError if other is not a list of 2 integers
    # @throws TypeError if other range is not overlap with this range
    def add(self, other) -> object:
        other = self.conv(other)
        
        if self.end < other.start or self.start > other.end:
            raise ValueError("other range must be overlap with this range")
        if self.start >= other.start and self.end <= other.end:
            return Range(other.start, other.end)
        if self.start >= other.start and self.end > other.end:
            return Range(other.start, self.end)
        if self.start < other.start and self.end <= other.end:
            return Range(self.start, other.end)
        if self.start < other.start and self.end > other.end:
            return Range(self.start, self.end)

remove

如果对其进行remove操作,即删除一个区间,也要考虑两个区间相交的情况。如果相交,则返回一个Range数组,其中可能0~2个区间。
在这里插入图片描述

    # remove the other range from this range.For example, [1, 5) remove [2, 3) is [1, 2) [3, 5).
    # @param other - the other range to remove from this range.the other range must be a Range object or a list of 2 integers
    # @return - a list of Range objects that are the result of removing other from this range
    # @throws TypeError if other is not a Range object or a list of integers
    # @throws ValueError if other is not a list of 2 integers
    def remove(self, other) -> list:
        other = self.conv(other)
        
        if self.end < other.start or self.start > other.end:
            return [self]
        if self.start >= other.start and self.end <= other.end:
            return []
        if self.start >= other.start and self.end > other.end:
            return [Range(other.end, self.end)]
        if self.start < other.start and self.end <= other.end:
            return [Range(self.start, other.start)]
        if self.start < other.start and self.end > other.end:
            return [Range(self.start, other.start), Range(other.end, self.end)]

Tools

在设计完Range类后,我们还需要解决下面两个问题:

  • 被修正的区间有哪些
  • 需要调整位置的区间有哪些
    在这里插入图片描述
    上图中标红的表示可能要调整区间的区域。
    对于没有没有需要调整的区域,则要找到临近的区域。比如上图中第一组中,[7,8)需要找到[5,6)这组区间。如果是add操作,则需要将[7,8)插入到区间数组的[5,6)后面。
#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-
# ======================================================================================================================
# Copyrigth (C) 2024 fangliang <304646673@qq.com>
#
# This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later
# version.
#
# ======================================================================================================================

from rangelist.range import Range

class Tools(object):          
        
    # search the index of the range which contains the value.First value is the index of the range where to compare with the value, 
    # second value is True if the range contains the value, False otherwise.  
    # @param ranges - the list of ranges
    # @param value - the value to search
    # @param start_index - the start index of the ranges to search
    # @return the index of the range where to compare with the value, True if the range contains the value, False otherwise
    @staticmethod
    def search(ranges, value, start_index = 0):
        if start_index < 0:
            start_index = 0
        end_index = len(ranges) - 1
        while start_index <= end_index:
            mid = (start_index + end_index) // 2
            if ranges[mid].start <= value and ranges[mid].end >= value:
                return (mid, True)
            elif ranges[mid].end < value:
                start_index = mid + 1
            else:
                end_index = mid - 1
        
        return (end_index, False)
    
    # search the index of the ranges which overlap with the search range.
    # First value is the index of the range where to compare with the value, second value is True if the range contains the value,
    # False otherwise.
    # @param ranges - the list of ranges
    # @param search_range - the range to search
    # @return a list of (index, overlap) of the ranges which overlap with the search range
    @staticmethod
    def search_overlap(ranges, search_range):
        if search_range.start == search_range.end:
            return []
        
        start = Tools.search(ranges, search_range.start)
        end = Tools.search(ranges, search_range.end, start[0])
        index_list = [start]
        for i in range(start[0]+1, end[0]+1):
            index_list.append((i, True))
        return index_list

search_overlap方法返回的数据如下:

[(-1, False), (0, True), (1, True)]

-1代表对比的区间(可能是新增或者删除)的起始值在第0个区间的左侧。
True和False表示区间是否会调整(因为有覆盖)。

RangeList

RangeList用于保存一组Range序列。
这题的解法也主要依赖于其add和remove方法。

add

    # add a range to the list.For example, [[1, 5)] add [2, 3) is [[1, 5)].[[1, 5)] add [6, 8) is [[1, 5) [6, 8)].
    # @param other - the other range to compare with
    # @return True if the other range is overlap with this range, False otherwise
    # @throws TypeError if other is not a Range object or a list of integers
    # @throws ValueError if other is not a list of 2 integers
    def add(self, other):
        other = Range.conv(other)
        
        indexes = Tools.search_overlap(self.ranges, other)
        del_start_index = -1
        for i in indexes:
            if i[1]:
                other = self.ranges[i[0]].add(other)
                if -1 == del_start_index:
                    del_start_index = i[0]
                    
        if -1 != del_start_index:
            del self.ranges[del_start_index : indexes[-1][0]+1]
            self.ranges.insert(del_start_index, other)
        elif len(indexes) > 0:
            self.ranges.insert(indexes[0][0]+1, other)
        
        return self

add方法会让一个Range不停“合并”被其覆盖的Range。然后删除被覆盖的Range,把新组合的Range插入到第一个覆盖的Range位置。
如果没有覆盖的区间,则在适当的位置插入。

remove

# remove the other range from this range.For example, [[1, 5) [10, 14)]] remove [2, 3) is [[1, 2) [3, 5) [10, 14)]].
    # @param other - the other range to remove from this range
    # @return - the new range after removing
    # @throws TypeError if other is not a Range object or a list of integers
    # @throws ValueError if other is not a list of 2 integers
    def remove(self, other):
        other = Range.conv(other)
        
        indexes = Tools.search_overlap(self.ranges, other)
        del_start_index = -1
        range_list_from_remove_all = []
        for i in indexes:
            if i[1]:
                range_list_from_remove = self.ranges[i[0]].remove(other)
                if range_list_from_remove != None:
                    range_list_from_remove_all.extend(range_list_from_remove)
                if -1 == del_start_index:
                    del_start_index = i[0]
        
        if -1 != del_start_index:
            del self.ranges[del_start_index : indexes[-1][0]+1]
            self.ranges[del_start_index:del_start_index] = range_list_from_remove_all
        
        return self

remove方法则是让Range List中Range不停remove待删除Range,最后把切割的Range重新插入到Range List中。

代码

https://github.com/f304646673/rangelist.git

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

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

相关文章

K8S搭建(centos)三、安装Docker

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

完美调试android-goldfish(linux kernel) aarch64的方法

环境要求 Mac m1Mac m1 中 虚拟机安装aarch64 ubuntu22.02Mac m1安装OrbStack&#xff0c;并在其中安装 ubuntu20.04&#xff08;x86_64&#xff09; 构建文件系统 在虚拟机 aarch64 ubuntu22.02中构建 安装必要的库 sudo apt-get install libncurses5-dev build-essenti…

工业空调协议转BACnet网关BA108

随着通讯技术和控制技术的发展&#xff0c;为了实现楼宇的高效、智能化管理&#xff0c;集中监控管理已成为楼宇智能管理发展的必然趋势。在此背景下&#xff0c;高性能的楼宇暖通数据传输解决方案——协议转换网关应运而生&#xff0c;广泛应用于楼宇自控和暖通空调系统应用中…

RK3399平台开发系列讲解(USB篇)USB协议层数据格式

🚀返回专栏总目录 文章目录 一、USB 资料二、协议层2.1、字节/位传输顺序2.2、SOP起始包2.3、SYNC同步域2.4、EOP 结束包(End of Packet)2.5、Packet内容2.5.1、PID:2.5.2、地址:2.5.3、帧号:2.5.4、数据域:

Linux——理解文件系统

目录 1、inode 2、硬链接、软链接 理解硬链接 软链接 3、静态库、动态库 静态库与动态库 生成静态库 生成动态库 使用动态库 运行动态库 使用外部库 库文件名称和引入库的名称 1、inode 使用ls -l命令不仅显示出了文件名&#xff0c;也可以显示出文件元数据 一行…

k8s 容器 java 应用内存限制不生效

一 k8s java 应用内存限制不生效 回顾&#xff1a;Linux杂谈之java命令 namespace负责资源隔离 cgroups负责资源限制 容器JVM最佳实践 Metaspace 是 非 Heap 内存 管理空间,那么 Heap 就是操作空间 JVM内存模型简介 隔离&#xff1a; 两个进程完全隔离感知&#xff1…

【STM32】STM32F4中USB的CDC虚拟串口(VCP)使用方法

文章目录 一、前言二、STM32CubeMX生成代码2.1 选择芯片2.2 配置相关模式2.3 设置时钟频率2.4 生成代码2.5 编译并下载代码2.6 结果2.7 问题 三、回环测试3.1 打开工程3.2 添加回环代码3.3 编译烧录并测试 四、出现问题和解决方法4.1 烧录总是要自己插拔USB4.2 自己生成的工程没…

141:vue+leaflet 利用高德逆地理编码,点击地图标记marker,popup地址信息

第141个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中利用高德逆地理编码,点击地图标记marker,popup地址信息 。主要利用高德地图的api将坐标转化为地址,然后在点击的位置,弹出弹窗,在里面显示出地址信息。 直接复制下面的 vue+leaflet源代码,操作2分钟…

14、Kafka ------ kafka 核心API 之 流API(就是把一个主题的消息 导流 到另一个主题里面去)

目录 kafka 核心API 之 流APIKafka流API的作用&#xff1a;流API的核心API&#xff1a;使用流API编程的大致步骤如下&#xff1a;代码演示 流API 用法MessageStream 流API 代码演示消息从 test1主题 导流到 test2主题演示使用匿名内部类对消息进行处理Topology 拓扑结构 讲解 代…

【AIGC】CLIP

CLIP的基本原理 对比学习&#xff1a; Clip使用对比学习来训练模型。对比学习的目标是通过将正样本&#xff08;相似的图像和文本对&#xff09;与负样本&#xff08;不相似的图像和文本对&#xff09;进行比较&#xff0c;从而使模型学会区分不同样本之间的差异。这有助于模型…

社区分享|百果园选择DataEase搭档蜜蜂微搭实现企业数据应用一体化

百果园&#xff0c;全称为深圳百果园实业&#xff08;集团&#xff09;股份有限公司&#xff0c;2001年12月成立于深圳&#xff0c;2002年开出中国第一家水果专卖店。截至2022年11月&#xff0c;百果园全国门店数量超过5600家&#xff0c;遍布全国140多个城市&#xff0c;消费会…

TensorRT英伟达官方示例解析(二)

系列文章目录 TensorRT英伟达官方示例解析&#xff08;一&#xff09; TensorRT英伟达官方示例解析&#xff08;二&#xff09; 文章目录 系列文章目录前言一、03-BuildEngineByTensorRTAPI1.1 建立 Logger&#xff08;日志记录器&#xff09;1.2 Builder 引擎构建器1.3 Netwo…

【SGX系列教程】(一)Intel-SGX SDK在ubuntu22.04下安装全流程

文章目录 一.概述1.1 SGX三大组件1.2 SGXDataCenterAttestationPrimitives 二.安装流程2.1 检查服务器是否支持SGX2.2 sgx硬件/软件开启方法2.3 sgx dirver驱动安装&#xff1b;2.3.1 linux-sgx-driver驱动程序2.3.2 Intel SGX Support in the Linux Kernel&#xff08;linux内…

【开源】基于JAVA的图书管理系统

目录 一、 系统介绍二、 功能模块2.1 登录注册模块2.1 图书馆模块2.2 图书类型模块2.3 图书模块2.4 图书借阅模块2.5 公告模块 三、 源码解析3.1 图书馆模块设计3.2 图书类型模块设计3.3 图书模块设计3.4 图书借阅模块设计3.5 公告模块设计 四、 免责说明 一、 系统介绍 图书管…

AnimatedDrawings:让绘图动起来

老样子&#xff0c;先上图片和官网。这个项目是让绘制的动画图片动起来&#xff0c;还能绑定人体的运动进行行为定制。 快速开始 1. 下载代码并进入文件夹&#xff0c;启动一键安装 git clone https://github.com/facebookresearch/AnimatedDrawings.gitcd AnimatedDrawingspip…

4小时精通MyBatisPlus框架

目录 1.介绍 2.快速入门 2.1.环境准备 2.2.快速开始 2.2.1引入依赖 2.2.2.定义Mapper ​编辑 2.2.3.测试 2.3.常见注解 ​编辑 2.3.1.TableName 2.3.2.TableId 2.3.3.TableField 2.4.常见配置 3.核心功能 3.1.条件构造器 3.1.1.QueryWrapper 3.1.2.UpdateWra…

SpringBoot3集成Zookeeper

标签&#xff1a;Zookeeper3.8 &#xff0c;Curator5.5&#xff1b; 一、简介 ZooKeeper是一个集中的服务&#xff0c;用于维护配置信息、命名、提供分布式同步、提供组服务。分布式应用程序以某种形式使用所有这些类型的服务。 二、环境搭建 1、修改配置文件 # 1、拷贝一份…

不合格机器人工程讲师再读《悉达多》-2024-

一次又一次失败的经历&#xff0c;让我对经典书籍的认同感越来越多&#xff0c;越来越觉得原来的自己是多么多么的无知和愚昧。 ----zhangrelay 唯物也好&#xff0c;唯心也罢&#xff0c;我们都要先热爱这个世界&#xff0c;然后才能在其中找到自己所热爱的事业。 ----zh…

用Netty手写Http/Https服务器

Netty是一个以事件驱动的异步通信网络框架&#xff0c;可以帮助我们实现多种协议的客户端和服务端通信&#xff0c;话不多说&#xff0c;上代码&#xff0c;需要引入下方依赖 <dependency><groupId>io.netty</groupId><artifactId>netty-all</artif…

C++虚函数表的简单理解

背景 C的三大特性&#xff1a;封装&#xff0c;继承&#xff0c;多态。其中基于多态特性的虚函数表概念是C开发者面试的长考题。今天梳理一下虚函数表的基本概念。 概念理解 为了实现 C 的多态&#xff0c;C 使用了一种动态绑定的技术。这个技术的核心是虚函数表。 虚表就是为…