《程序员面试金典(第6版)面试题 16.10. 生存人数(前缀和思想)

news2024/10/2 10:29:53

题目描述

给定 N 个人的出生年份和死亡年份,第 i 个人的出生年份为 birth[i],死亡年份为 death[i],实现一个方法以计算生存人数最多的年份。

  • 你可以假设所有人都出生于 1900 年至 2000 年(含 1900 和 2000 )之间。如果一个人在某一年的任意时期处于生存状态,那么他应该被纳入那一年的统计中。例如,生于 1908 年、死于 1909 年的人应当被列入 1908 年和 1909 年的计数。

  • 如果有多个年份生存人数相同且均为最大值,输出其中最小的年份。

示例:

输入:
birth = [1900, 1901, 1950]
death = [1948, 1951, 2000]
输出: 1901

提示:

  • 0 < birth.length == death.length <= 10000
  • birth[i] <= death[i]

解题思路与代码

这道题是一道中等偏简单的题。因为这道题我很快就有思路并且能较快的实现这道题,所以我把它归类为中等偏简单的题,hh。接下来来讲一下解题思路吧。

首先对于这道题来说,我们最重要的是要建立起一个数组,这个数组的作用其实就是去存储1900-2000年,每个年份存在的人数。

讲到这里相信大家心里应该有点数了,从这里,我们就可以衍生出两种做法,接下来,我们依次的循序渐进的介绍这两种方法。

方法一:累加,统计每一年的生存人数

  • 在这种方法里,我们首先创建一个大小为101的age数组。目的是映射1900-2000年所有年份存活的人数。

  • 之后我们用for循环遍历出生数组,并且用两个变量去记录当前这个人的出生年与死亡年都是多少。再用一个while循环去将age数组对应的年份的人数都+1

  • 在之后,我们再遍历一次age数组,找出最多生存人数的年份就好。

具体代码如下:

class Solution {
public:
    int maxAliveYear(vector<int>& birth, vector<int>& death) {
        int age[101] = {0}; // 用来存储当前在世人数。
        for(int i = 0; i < birth.size(); ++i){
            int by = birth[i] - 1900;
            int dy = death[i] - 1900;
            while(by <= dy){
                ++age[by];
                ++by;
            }
        }
        int maxPeople = 0;
        int result = 0;
        for(int i = 0; i < 101; ++i){
            if(maxPeople < age[i]){
                maxPeople = age[i];
                result = i + 1900;
            }
        }
        return result;
    }
};

在这里插入图片描述

复杂度分析

时间复杂度:

  • 第一个for循环遍历birth和death向量,复杂度为O(n),其中n为birth和death向量的大小,即人数。
  • 内部的while循环在最坏情况下,每个人都在整个时间段内活着,因此需要遍历整个年份范围(101年),那么最坏情况下复杂度为O(101)。
  • 第二个for循环遍历age数组,复杂度为O(101)。

总的时间复杂度为:O(n * 101 + 101)。由于101是一个常数,因此我们可以忽略它,所以总的时间复杂度为O(n)

空间复杂度:

  • age数组的空间复杂度为O(101)。
  • 其他变量(如maxPeople,result,by,dy等)都是常量级别的空间。

总的空间复杂度为O(101)。由于101是一个常数,我们可以忽略它,所以总的空间复杂度为O(1)

方法二: 优化,前缀和思想解决累积和或累积统计的问题

  • 这种方法的优化在于,优化掉了内在的while循环。与方法一的区别是,我们创建一个大小为102的age数组。为什么这么创建呢?是因为当一个人死亡的时候,它死亡的这一年也算它在这一年存在的。

  • 我们在用for循环遍历的时候,如果有人在这一年出生了,我们就把这一年它对应的出生age元素+1,它对应的死亡age + 1元素 -1。这就是我们为什么要创建102数组的原因。因为一个人它在2000年死亡的时候,2001年才会记作它不存在。所以数组的大小为102。

  • 最后我们在用一个for循环,去累加每一年的生存人数,然后去比较出一个最大生存人数的年份

具体的代码如下:

class Solution {
public:
    int maxAliveYear(vector<int>& birth, vector<int>& death) {
        int delta[102] = {0}; // 存储每年人口变化

        for (int i = 0; i < birth.size(); ++i) {
            delta[birth[i] - 1900]++; // 出生年份人数加一
            delta[death[i] - 1900 + 1]--; // 死亡年份的下一年人数减一
        }

        int maxPeople = 0;
        int currentAlive = 0;
        int result = 0;
        for (int i = 0; i < 101; ++i) {
            currentAlive += delta[i]; // 计算当前年份的生存人数
            if (currentAlive > maxPeople) {
                maxPeople = currentAlive;
                result = i + 1900;
            }
        }
        return result;
    }
};

在这里插入图片描述

复杂度分析

时间复杂度分析:

  • 第一个for循环遍历birth和death向量,复杂度为O(n),其中n为birth和death向量的大小,即人数。
  • 第二个for循环遍历age数组,复杂度为O(101)。

总的时间复杂度为:O(n + 101)。由于101是一个常数,我们可以忽略它,所以总的时间复杂度仍为O(n)。相比原始代码,时间复杂度没有改变,但实际计算量减少了很多。

空间复杂度分析:

  • age数组的空间复杂度为O(102)。
  • 其他变量(如max,curr,result等)都是常量级别的空间。

总的空间复杂度为O(102)。由于102是一个常数,我们可以忽略它,所以总的空间复杂度仍为O(1)。

总结

这道题的意义主要在于考察编程者如何处理和分析一组数据,以找到满足特定条件的解。在这个问题中,我们需要找到生存人数最多的年份。

这个问题可以帮助编程者练习以下几个方面的技能:

  • 数据处理:如何从给定的出生年份和死亡年份数据中提取有用的信息。
  • 循环和条件判断:如何遍历数据并进行逻辑判断以计算在每个年份的生存人数。
  • 状态累积:如何在遍历过程中累积生存人数,以便找到最大值和对应的年份。
  • 优化和算法思维:如何从暴力解法转向更优秀的算法,比如前缀和方法,从而提高计算效率。

总的来说,这道题的意义在于让编程者思考如何高效地处理和分析数据,找到符合特定条件的解。通过这道题,编程者可以提升自己的编程能力、算法思维和问题解决技巧。
最后的最后,如果你觉得我的这篇文章写的不错的话,请给我一个赞与收藏,关注我,我会继续给大家带来更多更优质的干货内容

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

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

相关文章

Spring源码解读——高频面试题

Spring IoC的底层实现 1.先通过createBeanFactory创建出一个Bean工厂&#xff08;DefaultListableBeanFactory&#xff09; 2.开始循环创建对象&#xff0c;因为容器中的bean默认都是单例的&#xff0c;所以优先通过getBean、doGetBean从容器中查找&#xff0c;如果找不到的…

LeetCode-1003. 检查替换后的词是否有效

题目链接 LeetCode-1003. 检查替换后的词是否有效 题目描述 题解 题解一&#xff08;Java&#xff09; 作者&#xff1a;仲景 题挺难懂的&#xff0c;很绕&#xff0c;然后读懂了就很简单了 就是说本来是一个字符串s&#xff0c;abc三个字符可以随便放在s原本字符串的左边或…

删除游戏-类似打家劫舍

198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 1 熟悉打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被…

java+微信小程序,实现chatgpt聊天小程序

chatgp持续火爆,然鹅会用的人其实挺少的,现在使用异步请求的方式,基本可以实现秒回复。并且还基于webSocket编写了一个微信小程序来进行交互,可以直接使用微信小程序来进行体验。 现在我将所有代码都上传了github(链接在文章结尾),大家可以clone下来,部署到服务器上,真…

shell命令

shell命令 打开文本编辑器(可以使用vi/vim创建文本),新建一个test.sh文件&#xff0c;输入一些代码&#xff0c;第一行为固定写法 #!/bin/bash echo hello word#!是一个约定的标记&#xff0c;他告诉系统这个脚本使用什么解释器执行 shell中注释 1.单行注释使用# 2.多行注释…

在Linux服务器上(非root权限)配置anaconda和pytorch的GPU环境

本人小白一枚&#xff0c;加入了导师的课题组之后使用学校的服务器开始炼丹&#xff0c;但是光是配环境就花了好几天&#xff0c;特此记录下。。。。 选择你趁手的工具 链接远程服务器的终端工具有很多&#xff0c;例如xshell等&#xff0c;我选择是的finalshell 下载教程 【…

敏捷ACP.敏捷估计与规划.Mike Cohn.

第一部分 传统规划失败的原因 vs 敏捷规划有效的原因 传统的项目规划方式往往会让我们失望。要回答-一个 新产品的范围/进度/资源的组合问题&#xff0c;传统规划过程不一定会产生令人非常满意的答案和最终产品。以下- -些论据可以支持这个结论: ●大约2/3的项目会显著超…

Linux设备驱动模型(一)

一、sysfs文件系统 sysfs是一个虚拟文件系统&#xff0c;将内核总的设备对象的链接关系&#xff0c;以文件目录的方式表示出来&#xff0c;并提对设备的供读写接口。 二、kobject kobject是内核中对象表示的基类&#xff0c;可以认为所有的内核对象都是一个kobject kobject单…

Docker 持久化存储 Bind mounts

Docker 持久化存储 Bind mounts Bind mounts 的 -v 与 --mount 区别启动容器基于bind mount挂载到容器中的非空目录只读 bind mountcompose 中使用 bind mount 官方文档&#xff1a;https://docs.docker.com/storage/bind-mounts/ Bind mounts 的 -v 与 --mount 区别 如果使用…

Origin如何使用基础功能?

文章目录 0.引言1.菜单栏2.工具栏 0.引言 因科研等多场景需要进行绘图处理&#xff0c;笔者对Origin进行了学习&#xff0c;本文通过《Origin 2022科学绘图与数据》及其配套素材结合网上相关资料进行学习笔记总结&#xff0c;本文对软件界面基础功能进行阐述。    1.菜单栏 …

【Linux内核解析-linux-5.14.10-内核源码注释】内核源码中宏定义理解

内核宏定义1 这是Linux内核中的start_kernel函数的一部分代码。它的作用是初始化内核的一些基本组件和数据结构。 asmlinkage: 这是一个函数声明修饰符&#xff0c;指示编译器把函数参数放在堆栈中&#xff0c;而不是寄存器中。 __visible: 这是另一个函数声明修饰符&#x…

第二十六章 碰撞体Collision(上)

在游戏世界中&#xff0c;游戏物体之间的交互都是通过“碰撞接触”来进行交互的。例如&#xff0c;攻击怪物则是主角与怪物的碰撞&#xff0c;触发机关则是主角与机关的碰撞。在DirectX课程中&#xff0c;我们也大致介绍过有关碰撞检测的内容。游戏世界中的3D模型的形状是非常复…

生成模型经典算法-VAEGAN(含Python源码例程)

生成模型 文章目录 生成模型1. 概述2. 生成模型典型结构-VAE&GAN2.1 VAE2.1.1 简介2.1.2 模型处理流程 2.2 GAN2.2.1 简介 2.2.2 生成对抗网络要点2.2.3 生成对抗网络的训练准则2.2.4 生成对抗网络模型处理流程 3.生成模型和判别模型在AIGC中的应用3.1 生成模型在AIGC中的应…

【SQL】面试篇之排序和分组练习

1587 银行账户概要 II 1587题目 # Write your MySQL query statement below select name, balance from (select u.account, name, sum(amount) as balancefrom Users uleft join Transactions ton u.account t.accountgroup by u.account ) temp where balance > 10000总…

给定一个文本文件,每行是一条股票信息,写程序提取出所有的股票代码

问题&#xff1a;给定一个文本文件&#xff0c;每行是一条股票信息&#xff0c;写程序提取出所有的股票代码。其中&#xff0c;股票代码规则是&#xff1a;6 位数字&#xff0c; 而且以.SH 或者.SZ 结尾。 文件内容示例&#xff1a; 2020-08-08;平安银行(000001.SZ);15.55;2940…

( 数组和矩阵) 645. 错误的集合 ——【Leetcode每日一题】

❓645. 错误的集合 难度&#xff1a;简单 集合 s 包含从 1 到 n 的整数。不幸的是&#xff0c;因为数据错误&#xff0c;导致集合里面某一个数字复制了成了集合里面的另外一个数字的值&#xff0c;导致集合 丢失了一个数字 并且 有一个数字重复 。 给定一个数组 nums 代表了…

教你如何快速安装下载EasyRecovery16最新版本

最近辛辛苦苦写的PPT报告被手残的我一个不小心给删除了&#xff0c;本来在空调屋里懂得瑟瑟发抖的我瞬间后背就被汗水浸湿了&#xff0c;好在懂点计算机知识&#xff0c;没有继续乱操作&#xff0c;在使用EasyRecovery后很快就找回来了! EasyRecovery 16&#xff08;有几个版本…

Git(六):基本命令(3):储藏、标签、拉取、子模块

目录 17、stash 储藏 17.2 描述 17.3 基本用法 18、tag 标签 18.1 描述 18.2 基本用法 19、fetch 获取 19.1 描述 19.2 基本用法 20、pull 整合 20.1 描述 20.2 基本用法 20.3 pull 与 fetch 的区别 21、push 更新推送 21.1 描述 21.2 基本用法 22、remote 管…

Vue性能优化

目录 Vue性能优化的必要性&#xff1a;Vue项目优化工具webpack-bundle-analyzerwebpack-bundle-analyzer作用&#xff1a;webpack-bundle-analyzer用法&#xff1a;webpack-bundle-analyzer运行&#xff1a;webpack-bundle-analyzer使用&#xff1a; vue项目性能优化代码模块化…

Tensorflow入门图像分类-猫狗分类-安卓

最近在温习 Tensorflow&#xff0c;写了一篇笔记&#xff0c;记录了使用 Tensorflow 训练一个猫狗图像分类器的模型并在安卓应用上使用的全过程。 一、数据集准备 1.1 数据集来源 我采用的是微软的猫狗数据集&#xff0c;链接&#xff1a;Download Kaggle Cats and Dogs Datas…