《程序员面试金典(第6版)》面试题 10.10. 数字流的秩
题目描述
假设你正在读取一串整数。每隔一段时间,你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。请实现数据结构和算法来支持这些操作,也就是说:
-
实现 track(int x) 方法,每读入一个数字都会调用该方法;
-
实现 getRankOfNumber(int x) 方法,返回小于或等于 x 的值的个数。
注意:本题相对原题稍作改动
示例:
输入:
["StreamRank", "getRankOfNumber", "track", "getRankOfNumber"]
[[], [1], [0], [0]]
输出:
[null,0,null,1]
提示:
- x <= 50000
- track 和 getRankOfNumber 方法的调用次数均不超过 2000 次
解题思路与代码
这道题,其实有一种特别简单的做法,和一个需要你有一定知识储备的做法,前者是我自己想出来的,时间复杂度偏高,但是空间复杂度比较低。后者呢?时间复杂度偏低,但是空间复杂度缺偏高,可以说,各有优点吧。
对于后者,用到了一种叫做树状数组的数据结构,你也可以理解为一种算法的思想,或者技巧。掌握这种数据结构的过程可能比较难,你首先得知道树状数组是个什么东西,才能知道里面的操作为什么这么去做。
那我们就一步一步的来,教会大家如何用这两种方法,来解决这道题!
方法1,使用sort函数帮助我们排序。
-
顾名思义,我们用一个vector数组来帮助我们去存储数据。
-
每次读取一个数字,我们就把这个数据添加到数组的最后面,然后对整个数组进行排序(用sort函数排序)
-
这样,track方法就编写完成啦。
-
紧接着,我们来编写一下getRankOfNumber方法,其实这个方法的实现也很简单。因为我们已经把读到的所有元素进行排序了,这个时候,只需要从后往前去遍历整个数组,找到第一个小于等于x的元素,返回这个元素的下标 + 1,就行了。如果找不到这样的元素,那就返回0就好啦。两个方法其实都不难的
具体的代码如下:
class StreamRank {
public:vector<int> vec;StreamRank() {}void track(int x) {vec.push_back(x);sort(vec.begin(),vec.end());}int getRankOfNumber(int x) {int pre = vec.size;for(int i = vec.size()-1; i >=0; --i){if(vec[i] <= x){pre = i;return pre + 1;}}return 0;}
};/*** Your StreamRank object will be instantiated and called as such:* StreamRank* obj = new StreamRank();* obj->track(x);* int param_2 = obj->getRankOfNumber(x);*/
复杂度分析
对于给出的 StreamRank 类,我们可以分析其方法的时间复杂度和空间复杂度如下:
track 方法:
- 时间复杂度:在每次调用 track 方法时,都会对 vec 进行排序。C++ 标准库中的 sort 函数的平均时间复杂度为 O(nlogn),其中 n 是数组的长度。因此,track 方法的时间复杂度为 O(nlogn)。
- 空间复杂度:track 方法中,除了向 vec 添加元素外,没有使用额外的空间。因此,空间复杂度为 O(1)。
getRankOfNumber 方法:
- 时间复杂度:在最坏情况下,getRankOfNumber 方法需要遍历整个 vec。因此,它的时间复杂度为 O(n),其中 n 是数组的长度。
- 空间复杂度:getRankOfNumber 方法中没有使用额外的空间,所以空间复杂度为 O(1)。
StreamRank 类的总空间复杂度:
StreamRank 类中使用了一个 vector 容器来存储元素,其空间复杂度为 O(n),其中 n 是存储的元素个数。
总结:StreamRank 类的时间复杂度主要取决于 track 方法的 O(n*logn),而空间复杂度主要取决于存储元素的 vector,为 O(n)。
方法二,使用树状数组来解决问题
首先,请允许在下为大家讲解一下到底什么才是树状数组。树状数组的特点是什么?
-
树状数组是一种用于维护数列前缀和的数据结构,它可以在 O(logn) 的时间复杂度内修改单个元素的值,以及查询某个区间的元素和。
-
树状数组的特点其实就是,在单点修改 ,和区间查询,它所需要写的代码量少,与时间复杂度低而闻名。
-
但是树状数组需要的空间复杂度很高,原因是,假如我们的查询的数字x的大小有100000这么大,那么这个树状数组的节点个数就得有100001个这么多。
因为如果真的要在这里给大家讲清楚什么是树状数组的话,这篇文章得7000多字,所以如果希望了解树状数组这种数据结构了话,请移步我写的这篇文章扫清盲点:带你学习 树状数组 这种数据结构
-
在这道题中,因为题目给定范围是x <= 50000,这意味着输入的整数最大值为50000。而我们有50001个数据要处理。但是在树状数组中,我们需要将从1这个位置开始进行初始化,而数组呢,是从0开始的,所以我们要将我们所有输入的整数x去自增1,使其范围为1到50001。
-
这样,我们就需要用一个大小为50002的数组来容纳所有可能的值(包括下标为0的位置,虽然下标为0,这个位置不需要使用)
-
lowbit(int x):这是一个辅助函数,用于计算整数 x 的二进制表示中最低位的 1 所对应的数值。在 C++ 中,可以用 x & (-x) 快速计算 lowbit(x)。
-
在 track 方法中,我们需要保证更新过程不会超出数组的范围。因此,我们将条件设置为 x <= 50001,这样在更新过程中 x 的最大值为 50001,不会越界。
当我们向树状数组中插入一个数 x 时,实际上需要更新多个数组元素。具体来说,我们首先将 x 自增 1(因为树状数组的下标从 1 开始),然后进入一个 while 循环,不断更新树状数组中的元素。每次更新后,我们都将 x 加上它的 lowbit(x),直到 x 超过数组的最大下标(在这个例子中是 50001)。 -
getRankOfNumber(int x):这个函数用于计算小于等于 x 的值的个数。与 track 函数类似,我们首先对 x 执行自增操作(++x)。然后使用一个 while 循环进行前缀和查询。循环中,我们累加 v[x] 的值,并通过 x -= lowbit(x) 来更新 x。当 x 变为 0 时,循环结束,返回累加的结果。
具体的代码实现如下:
class StreamRank {int lowbit(int x){return x & (-x) ;}
public:vector<int> arrayTree;StreamRank(): arrayTree(50002,0) {}void track(int x) {++x; while(x <= 50001){++arrayTree[x];x = x + lowbit(x);}}int getRankOfNumber(int x) {++x;int ans = 0;while (x){ans += arrayTree[x];x -= lowbit(x);}return ans;}};
复杂度分析:
时间复杂度:
- track 方法:在这个方法中,我们更新树状数组。每次更新一个节点时,我们需要沿着树向上更新所有关联的父节点。由于树状数组的特性,这个过程的时间复杂度为 O(logN),其中 N 是数组的大小。在本例中,N = 50002,所以时间复杂度为 O(log50002)。而我们可能调用track 方法n次,所以时间复杂度为 O(n * log50002)
- getRankOfNumber 方法:在这个方法中,我们从树状数组中查询累积和。与更新过程类似,查询过程也需要沿着树向上查询所有关联的父节点,时间复杂度同样为 O(logN),即 O(log50002)。
空间复杂度:
- 由于我们使用了一个大小为 50002 的数组来存储树状数组,因此空间复杂度为 O(N),即 O(50002)。
综上,这段代码的时间复杂度为 O(n * log50002),空间复杂度为 O(50002)。
总结
这道题我给出了两种解决方法,它们各自有各自的优缺点。总的来说,这是一道不错的题目,难度适中。
如果大家觉得我这篇文章写的好的话,请关注我,给我个赞和收藏,您的支持,是我持续输出优质内容的无上动力!
相关文章:

《程序员面试金典(第6版)》面试题 10.10. 数字流的秩
题目描述 假设你正在读取一串整数。每隔一段时间,你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。请实现数据结构和算法来支持这些操作,也就是说: 实现 track(int x) 方法,每读入一个数字都会调用该方法; 实现 g…...

智能洗地机好用吗?值得入手的洗地机推荐
洗地机是一款高效的地面清洁设备,不仅可以很好清理地面不同形态的干湿垃圾,还减少了人工和水资源的浪费,是我们日常生活中必不可少的清洁工具。作为以一位评测博主,很多朋友咨询我在选购洗地机时应该注意哪些要点,有哪…...

Spring Security实战(一)——基于内存和数据库模型的认证与授权
目录 简介 一、初识Spring Security(入门案例) (1)新建project (2)选择依赖 (3)编写一个 HelloController (4)启动项目,访问localhost:8080…...
轻松掌握FFmpeg编程:从架构到实践
轻松掌握FFmpeg编程:从架构到实践 (Master FFmpeg Programming with Ease: From Architecture to Practice 引言 (Introduction)FFmpeg简介与应用场景 (Brief Introduction and Application Scenarios of FFmpeg)为什么选择FFmpeg进行音视频处理 (Why Choose FFmpeg…...
桌面应用程序开发攻略(初步了解)
什么是桌面应用程序? 桌面应用开发是指为桌面计算机或其他类似设备(如服务器)开发软件应用程序的过程。桌面应用通常是独立于浏览器运行的,并且可以在操作系统的桌面或应用程序菜单中找到。桌面应用可以使用各种编程语言开发&…...

【李老师云计算】HBase+Zookeeper部署及Maven访问(HBase集群实验)
索引 前言1. Zookeeper1.1 主机下载Zookeeper安装包1.2 主机解压Zookeeper1.3 ★解决解压后文件缺失1.4 主机配置Zookeeper文件1.4.1 配置zoo_sample.cfg文件1.4.2 配置/data/myid文件 1.5 主机传输Zookeeper文件到从机1.6 从机修改Zookeeper文件1.6.1 修改zoo.cfg文件1.6.2 修…...

第11章_常用类和基础API
第11章_常用类和基础API 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 本章专题与脉络 1. 字符串相关类之不可变字符序列:String 1.1 String的特性 java.lang.String 类代表字符串…...

Java语言数据类型与c语言数据类型的不同
目录 一、c语言数据类型 1.基本类型: 2.枚举类型: 3.空类型: 4.派生类型: 二、C语言编程需要注意的64位和32机器的区别 三、 不同之处 一、c语言数据类型 首先,先来整体介绍一下C语言的数据类型分类。 1.基…...
C# Replace()、Trim()、Split()、Substring()、IndexOf() 、 LastIndexOf()函数
目录 一、Replace() 二、Trim() 三、Split() 四、Substring() 五、IndexOf() 六、LastIndexOf() 一、Replace() 在C#中,Replace()是一个字符串方法,用于将指定的字符或子字符串替换为另一个字符或字符串。下面是一些Replace()方法的常见用法和示例…...

C++类的理解与类型名,类的成员,两种定义方式,类的访问限定符,成员访问,作用域与实例化对象
面向过程和面向对象初步认识 C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题 C是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成 面向…...
【华为OD机试真题 C++】1051 - 处理器问题 | 机试题+算法思路+考点+代码解析
文章目录 一、题目🔸题目描述🔸输入输出🔸样例1🔸样例2 二、题目解析三、代码参考 作者:KJ.JK 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 &…...
Linux 常用操作命令大全
一、基础知识 1.1 Linux系统的文件结构 /bin 二进制文件,系统常规命令 /boot 系统启动分区,系统启动时读取的文件 /dev 设备文件 /etc 大多数配置文件 /home 普通用户的家目录 /lib 32位函数库 /lib64 64位库 /media 手动临时挂载点 /mnt 手动临时挂载点…...

Git使用教程
Git 目标 Git简介【了解】 使用Git管理文件版本【重点】 远程仓库使用【掌握】 分支管理【重点】 远程仓库【掌握】 一、Git简介 1、版本控制系统简介 1.1、版本控制前生今世 版本控制系统Version Control Systems,简称 VCS是将『什么时候、谁、对什么文件…...

substrate中打印调试信息的多种方式详解
目录 1. 获取substrate-node-template代码2. 添加一个用于测试的pallet至依赖到pallets目录3. log方式来输出信息3.1 将log依赖添到cargo.toml文件3.2 log-test/src/lib.rs修改call方法 3.3 polkadot.js.调用测试函数do_something_log_test4. printable trait方式来输出信息4.1…...

Disentangled Graph Collaborative Filtering
代码地址:https://github.com/ xiangwang1223/disentangled_graph_collaborative_filtering Background: 现有模型在很大程度上以统一的方式对用户-物品关系进行建模(将模型看做黑盒,历史交互作为输入,Embedding作为输出。)&…...

Nginx快速上手
Nginx快速上手 OVERVIEW Nginx快速上手一、基本概念1.Nginx初步认识2.正向/反向代理(1)正向代理(2)反向代理 二、Nginx 安装和配置1.安装2.Nginx指令3.Nginx配置 三、Nginx的使用1.Web服务器(1)静态网页存储…...
【设计模式】实际场景解释策略模式与工厂模式的应用
文章目录 前言策略模式概念场景示例 工厂模式概念场景示例 策略模式与工厂模式的比较相同点不同点 总结 前言 策略模式和工厂模式是常见的设计模式,它们可以帮助我们更好地组织和管理代码,提高代码的可维护性和可扩展性。 在本篇博客中,我将…...

外包干了三年,算是废了...
先说一下自己的情况。大专生,19年通过校招进入湖南某软件公司,干了接近3年的测试,今年年上旬,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了三年,…...

九龙证券|光模块概念股封单资金超3亿元,传媒板块涨停潮来袭
今天A股三大股指低开低走。沪深两市收盘共37股涨停。剔除4只ST股,合计33股涨停。另外,10股封板未遂,整体封板率为78.72%。 涨停战场: 华工科技封单资金超3亿元 从收盘涨停板封单量来看,同方股份封单量最高࿰…...
[ES6] 数组
[ES6] 数组 数组的创建类数组对象可迭代对象的转换 扩展方法findfindIndexfillcopyWithinentrieskeysvaluesincludesflatflatMap 扩展运算符复制数组合并数组 数组缓冲区创建数组缓冲区视图创建 定型数组创建通过数组缓冲区生成通过构造函数 定型数组特性 拷贝浅拷贝深拷贝 数组…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...

听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...

免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...