力扣面试经典150 —— 11-15题

  • 力扣面试经典150题
  • 在 VScode 中安装 LeetCode 插件即可使用 VScode 刷题,安装 Debug LeetCode 插件可以免费 debug
  • 本文使用 python 语言解题,文中 “数组” 通常指 python 列表;文中 “指针” 通常指 python 列表索引

文章目录

  • 11. [中等] H指数
    • 11.1 解法1:暴力法
    • 11.2 解法2:计数排序
    • 11.3 解法3:排序
  • 12. [中等] O(1) 时间插入、删除和获取随机元素
    • 12.1 解法1:哈希表+变长数组
  • 13. [中等] 除自身以外的数组的乘积
    • 13.1 解法1:左右乘积列表
    • 13.2 解法2:左右乘积列表
  • 14. [中等] 加油站
    • 14.1 解法1:一次遍历
  • 15. [困难] 分发糖果
    • 15.1 解法1:两次遍历

11. [中等] H指数

  • 题目链接
  • 标签:数组、计数排序、排序
    在这里插入图片描述

11.1 解法1:暴力法

  • 根据题目可知,h 指数不能超过论文发表总数,也不能超过最高引用此次,其最大值为 min(max(citations), len(citations))。从该最大可能取值开始反向遍历所有可能取值 i,统计引用次数 >=i 的论文数量 paper_cnt,直到找到满足 h 指数的定义(即 paper_cnt>=i)的取值为止。这是一种带剪枝的暴力搜索方法
    class Solution:
        def hIndex(self, citations: List[int]) -> int:
            # 根据定义,h 指数的理论最大值
            max_h = min(max(citations), len(citations))
    
            # 从 max_h 开始反向遍历考察所有 h 指数的可能取值 i 
            for i in range(max_h, -1, -1):
                # 统计引用次数 >= i 的论文数量
                paper_cnt = 0
                for cite in citations:
                    if cite >= i:
                        paper_cnt += 1
    
                # 满足 h 指数定义则返回
                if paper_cnt >= i:
                    return i
            return 0
    
  • 时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( 1 ) O(1) O(1)

11.2 解法2:计数排序

  • 以上暴力法中,对于每一个候选的 h 指数取值都做了一次遍历计数,因此时间复杂度高。一种优化方式是,先用过一次遍历完成所有计数操作,再通过另一次和暴力法相同的反向遍历确定 h 指数的值。具体而言,第一次遍历中我们用 defaultdict 统计引用量为 h 指数各可能取值i 的论文数量,之后在反向遍历时通过求和得到引用量 >=i 的论文数量
  • 这种方法通过引入 O ( n ) O(n) O(n) 的额外存储空间,将时间复杂度从 O ( n 2 ) O(n^2) O(n2) 降低到 O ( n ) O(n) O(n)
    class Solution:    
        def hIndex(self, citations: List[int]) -> int:
            # 根据定义,h 指数的理论最大值
            max_h = min(max(citations), len(citations))
    
            # 用 counter 统计引用量 >= 不同 cite 值的论文数量
            from collections import defaultdict
            counter = defaultdict(int)
            for cite in citations:
                cite = max_h if cite > max_h else cite
                counter[cite] += 1
    
            # 从 max_h 开始反向遍历考察所有 h 指数的可能取值 i 
            tot = 0     
            for i in range(max_h, -1, -1):
                tot += counter[i]   # 引用量不少于 i 次的论文总数
                if tot >= i:        # 满足 h 指数定义则返回
                    return i
            return 0
    
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

11.3 解法3:排序

  • 先初始化 h=0,然后把引用次数 citations 排序并大到小遍历。如果当前 h 指数为 h 并且在遍历过程中找到当前值 citations[i]>h,则说明我们找到了一篇被引用了至少 h+1 次的论文,所以 h+=1。继续遍历直到 h 无法继续增大后返回即可
    class Solution:
        def hIndex(self, citations: List[int]) -> int:
            sorted_citation = sorted(citations, reverse = True)
            h = 0; i = 0; n = len(citations)
            while i < n and sorted_citation[i] > h:
                h += 1
                i += 1
            return h
    
  • 时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度 O ( l o g n ) O(logn) O(logn)(这两个都是排序算法的复杂度)

12. [中等] O(1) 时间插入、删除和获取随机元素

  • 题目链接
  • 标签:设计、数组、哈希表、数学
    在这里插入图片描述

12.1 解法1:哈希表+变长数组

  • 要求实现插入删除获取随机元素操作的平均时间复杂度为 O ( 1 ) O(1) O(1)
    1. 变长数组:可以在 O ( 1 ) O(1) O(1) 的时间内完成获取随机元素操作。但是由于需要 O ( n ) O(n) O(n) 时间判断元素是否存在,因此无法满足插入和删除的时间复杂度要求
    2. 哈希表:哈希表的核心思想,是通过函数函数把元素映射到存储位置索引,这样就能在 O ( 1 ) O(1) O(1) 的时间内判断元素是否存在,或找到元素存储位置进行插入或删除。但哈希表无法在 O ( 1 ) O(1) O(1) 时间内获取当前全体元素,因此无法满足随机取元素的时间复杂度要求
  • 通过结合变长数组和哈希表,可以实现题目要求
    class RandomizedSet:
        def __init__(self):
            from collections import defaultdict
            import random
            self.item = []  # 在此存储元素
            self.idx = {}   # 哈希表,将元素映射到其在 self.item 中的索引位置
    
        def insert(self, val: int) -> bool:
            if val in self.idx:
                return False
            self.item.append(val)
            self.idx[val] = len(self.item) - 1
            return True
    
        def remove(self, val: int) -> bool:
            if not val in self.idx:
                return False
            idx_val = self.idx[val]         
            item_last = self.item[-1]       
            self.item[idx_val] = item_last  # self.item 中,用 item_last 替换目标元素
            self.idx[item_last] = idx_val   # self.idx 哈希表中,更新 item_last 对应的索引位置
            self.item.pop()                 # 弹出已经移动到 idx_val 处的 item_last
            del self.idx[val]               # 删除目标元素在哈希表中的索引
            return True
    
        def getRandom(self) -> int:
            return random.choice(self.item)
           
    # Your RandomizedSet object will be instantiated and called as such:
    # obj = RandomizedSet()
    # param_1 = obj.insert(val)
    # param_2 = obj.remove(val)
    # param_3 = obj.getRandom()
    # @lc code=end
    

13. [中等] 除自身以外的数组的乘积

  • 题目链接
  • 标签:数组、前缀和
    在这里插入图片描述

13.1 解法1:左右乘积列表

  • 用双指针同时从左右开始遍历列表,将左侧所有数字的乘积(前缀积)和右侧所有数字的乘积(后缀积)存储到两个辅助列表中。最后将两个辅助列表对应位置相乘得到结果
    class Solution:
        def productExceptSelf(self, nums: List[int]) -> List[int]:
            # pre_prods 存储每个索引位置所有前驱元素乘积
            # post_prods 存储每个索引位置所有后继元素乘积
            pre_prod, post_prod = 1, 1
            pre_prods, post_prods = [], []
            left, right = 0, -1
            for i in range(len(nums)):
                pre_prods.append(pre_prod)
                post_prods.append(post_prod)
                pre_prod *= nums[left]
                post_prod *= nums[right]
                left += 1
                right -= 1
            post_prods.reverse() 
            
            # 输出中每个索引位置,取 pre_prods 和 post_prods 对应位置元素相乘即可
            res = []
            for i in range(len(nums)):
                res.append(pre_prods[i] * post_prods[i])
            
            return res
    
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

13.2 解法2:左右乘积列表

  • 以上方法需要构造存储前缀积和后缀积的两个辅助列表。为了减少空间复杂度,可以先构造前缀积列表,然后在计算后续元素乘积时直接将其乘到前缀积列表中并作为输出。这样可以把空间复杂度降低到 O ( 1 ) O(1) O(1)(不考虑输出数组)
    class Solution:
        def productExceptSelf(self, nums: List[int]) -> List[int]:
            # pre_prods 存储每个索引位置所有前驱元素乘积
            pre_prod = 1
            res = []
            for num in nums:
                res.append(pre_prod)
                pre_prod *= num
            
            # 再把后续元素乘积直接乘到 res 的对应位置上,实现 O(1) 的空间复杂度
            post_prod = 1
            for i in range(len(nums)-1, -1, -1):
                res[i] *= post_prod
                post_prod *= nums[i]
            
            return res
    
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)

14. [中等] 加油站

  • 题目链接
  • 标签:贪心、数组
    在这里插入图片描述

14.1 解法1:一次遍历

  • 最直接的思想是依次把每一个加油站作为起点进行考察,直到找到能够绕行一周的加油站为止,但是这种暴力解法时间复杂度高。可以通过减小被检查的加油站数目来降低时间复杂度。
  • 注意到这样一个事实:如果从 x 加油站出发最多只能到达 z 加油站,那么从 x 和 z 之间的 y 加油站出发一定无法到达超过 z 的位置。这是因为从 x 出发到达 y 时可能车里还有剩余燃油,直接从 y 出发不可能走得更远
  • 我们可以从第 0 个加油站开始判断能否环绕一周;如果不能,就从第一个无法到达的加油站开始继续检查。
    class Solution:
        def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
            def _available_cnt(idx):
                # 计算从 idx 开始可以连续到达的加油站数量
                cnt, gas_left = 0, 0
                for i in range(n):
                    gas_left += delta[(idx + i) % n] 
                    if gas_left < 0:
                        return cnt
                    cnt += 1
                return cnt
    
            # 从各个加油站出发到下一个加油站导致的油量变化
            delta = [g - c for g, c in zip(gas, cost)]  
    
            # 检查从各个加油站出发能否环绕一周;不能则从第一个无法到达的加油站开始继续检查
            n, idx = len(gas), 0
            while idx < n:
                cnt = _available_cnt(idx)
                # 找到可能访问所有加油站的起点则返回
                if cnt == n:
                    return idx
                idx += cnt + 1
            return -1
    
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)

15. [困难] 分发糖果

  • 题目链接
  • 标签:贪心、数组
    在这里插入图片描述

15.1 解法1:两次遍历

  • “相邻的孩子中,评分高的孩子必须获得更多的糖果” 这一句话可以拆分成两个规则
    1. 左规则:ratings[i-1]<ratings[i] 时,i 号的糖果比 i-1 号多
    2. 右规则:ratings[i]>ratings[i+1] 时,i 号的糖果比 i+1 号多
  • 单独处理其中任意一个规则是简单的,以左规则为例,初始化分配糖果数为1,从左到右遍历,若分数递增则分配糖果数+1,反之重置为1。右规则同理。
    1. 对于仅满足左规则的分配数组 L,其在每一个分数递增段都从1开始递增,其余部分全是1
    2. 对于仅满足右规则的分配数组 R,其在每一个分数递减段都递减到1,其余部分全是1
  • 经过两次遍历得到 LR 后,直接给第 i i i 个小孩分配 max(L[i], R[i]) 颗糖果即可。为了分析这种操作的正确性,假设 L [ i ] > R [ i ] L[i] > R[i] L[i]>R[i],则 max ⁡ ( L [ i ] , R [ i ] ) = L [ i ] \max(L[i], R[i]) = L[i] max(L[i],R[i])=L[i],此时左规则一定满足,考虑右规则
    1. ratings[i]>ratings[i+1],此时分配数量 L [ i ] > R [ i ] > R [ i + 1 ] L[i]>R[i]>R[i+1] L[i]>R[i]>R[i+1] 一定满足右规则
    2. ratings[i-1]>ratings[i],这意味着 i i i 处于一个递减序列内,此时 L [ i ] = 1 L[i]=1 L[i]=1,不可能有 L [ i ] > R [ i ] L[i] > R[i] L[i]>R[i],故增加给第 i i i 个小孩的糖果数量不会导致在 i − 1 i-1 i1 处违反右规则
  • 综上,给出如下的求解代码
    class Solution:
        def candy(self, ratings: List[int]) -> int:
            n = len(ratings)
    
            # 仅考虑左规则对应的最少糖果分配
            left = [1, ]
            for i in range(n-1):
                left.append(left[i] + 1 if ratings[i+1] > ratings[i] else 1)
                
            # 仅考虑右规则对应的最少糖果分配
            ratings.reverse()
            right = [1, ]
            for i in range(n-1):
                right.append(right[i] + 1 if ratings[i+1] > ratings[i] else 1)
            right.reverse()
    
            # max 操作确定每个索引处同时满足左右规则的糖果数,求和
            return sum([max(l, r) for l, r in zip(left, right)])
    
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

十大排序算法(冒泡排序、插入排序、选择排序、希尔排序、堆排序、快排、归并排序、桶排序、计数排序、基数排序)

目录 一、冒泡排序&#xff1a; 二、插入排序&#xff1a; 三、选择排序&#xff1a; 四、希尔排序&#xff1a; 五、堆排序&#xff1a; 六、快速排序&#xff1a; 6.1挖坑法&#xff1a; 6.2左右指针法 6.3前后指针法&#xff1a; 七、归并排序&#xff1a; 八、桶…

回溯算法10-非递减子序列(Java/set去重操作)

10.非递减子序列 题目描述 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也可以视作递增序列的一…

PyQt6实战1

创建一个json处理的小工具 功能&#xff1a; 1.json格式化 2.jsonpath提取数据 3.保存文件 main.py from PyQt6.QtGui import QFocusEvent from PyQt6.QtWidgets import * from PyQt6.QtCore import * from PyQt6.QtGui import * import sys import json import time impo…

【笔记】原油阳谋论

文章目录 石油的属性能源属性各国石油替代 金融属性黄金石油美元 油价历史油价传导路径 石油供需格局与发展供需格局各国状况美国俄罗斯沙特 产油国困境运输 分析格局分析供需平衡分析价差分析价差概念基本面的跨区模型跨区模型下的价差逻辑 长中短三期分析长期视角——供应看投…

2024年腾讯云99元1年服务器_新老同享_续费99元一年

良心腾讯云推出99元一年服务器&#xff0c;新用户和老用户均可以购买&#xff0c;续费不涨价&#xff0c;续费也是99元&#xff0c;配置为轻量2核2G4M、50GB SSD盘、300GB月流量、4M带宽&#xff1a;优惠价格99元一年&#xff0c;续费99元&#xff0c;官方活动页面 txybk.com/g…

美洲狮优化算法(Puma Optimizar Algorithm ,POA)求解机器人栅格地图最短路径规划(提供MATLAB代码)

一、美洲狮优化算法 美洲狮优化算法&#xff08;Puma Optimizar Algorithm &#xff0c;POA&#xff09;由Benyamin Abdollahzadeh等人于2024年提出&#xff0c;其灵感来自美洲狮的智慧和生活。在该算法中&#xff0c;在探索和开发的每个阶段都提出了独特而强大的机制&#xf…

【JavaSE】抽象类与接口

Object 类 类 java.lang.Object是类层次结构的根类&#xff0c;即所有类的父类。 除Object类之外的任何一个Java类&#xff0c;全部直接或间接的继承于Object类。由此&#xff0c;Object类也被称为根父类。Object类中声明的成员具有通用性&#xff0c;并且Object类中没有声明…

Linux 理解进程

目录 一、基本概念 二、描述进程-PCB 1、task_struct-PCB的一种 2、task_ struct内容分类 三、组织进程 四、查看进程 1、ps指令 2、top命令 3、/proc文件系统 4、在/proc文件中查看指定进程 5、进程的工作目录 五、通过系统调用获取进程标示符 1、getpid()/get…

消息队列 MQ

文章目录 1. MQ 相关概念1.1 什么是 MQ1.2 为什么要用 MQ1.3 MQ 分类1.4 MQ 的选择 1. MQ 相关概念 1.1 什么是 MQ MQ(message queue)&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO 先入先出&#xff0c;只不过队列中存放的内容是 message 而已&#x…

选修-单片机作业第1/2次

第一次作业 第二次作业 1、51 系列单片机片内由哪几个部分组成&#xff1f;各个部件的最主要功能是什么&#xff1f; 51系列单片机的内部主要由以下几个部分组成&#xff0c;每个部件的主要功能如下&#xff1a; 1. **中央处理器&#xff08;CPU&#xff09;**&#xff1a;这是…

uniapp隐藏状态栏并强制横屏

uniapp隐藏状态栏并强制横屏 1.manifest.json中&#xff1a; "screenOrientation": ["landscape-primary", //可选&#xff0c;字符串类型&#xff0c;支持横屏"landscape-secondary" //可选&#xff0c;字符串类型&#xff0c;支持反向横屏]…

算法 环形数组是否存在循环 力扣执行速度击败100%

目录 题目 leetcode 457 求解思路 代码 结果 题目 leetcode 457 存在一个不含 0 的 环形 数组 nums &#xff0c;每个 nums[i] 都表示位于下标 i 的角色应该向前或向后移动的下标个数&#xff1a; 如果 nums[i] 是正数&#xff0c;向前&#xff08;下标递增方向&#xff0…

每日一题 — 三数之和

15. 三数之和 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 双指针思想先给数组排序然后固定一个数、再设left、right指针&#xff0c;nums[left] nums[right] -nums[a]大于的话right--&#xff0c;小于的话left每次处理完left、right之后需要判断去重i也需要判…

计算机网络(五)

网络层 网络层的主要目的是实现网络互连&#xff0c;进而实现数据包在各网络之间的传输。 要实现网络层&#xff0c;主要解决三个问题&#xff1a; ①网络层向运输层提供怎样的服务&#xff1f;&#xff08;“可靠传输“、”不可靠传输“&#xff09; ②网络层寻址 ③路由选择…

乡村治理深度解析:策略、挑战与解决方案

毋庸置疑&#xff0c;在今天这个崭新的时代&#xff0c;乡村治理的过程已然向我们发出了挑战。为了迎难而上&#xff0c;我们必须摒弃陈旧观念&#xff0c;勇敢迎接并大胆尝试探索与实践新的思路&#xff01;为了达到这一宏伟目标&#xff0c;我们需要首先廓清如下关键概念&…

第九个实验:一维数组和二维字符串数组的输入而输出

实验内容: 新建一维数组 新建二维字符串数组 输入内容,运行结果,在输出界面中显示输入的内容 第一步:新建项目 第二步:编程 添加一个INT数控件和字符串控件 修改控件: 复制前面板控件

Linux 之九:CentOS 上 Tomcat 安装、SpringBoot 项目打包和部署

安装 Tomcat 下载 a. 方式一&#xff1a;可以在windows 真机上下载后&#xff0c;再上传到服务器 b. 方式二&#xff1a;可以在服务器端使用 wget 下载命令来下载 登录官网https://tomcat.apache.org/download-90.cgi&#xff0c;选择 linux 版本 右键&#xff0c;获取下载链接…

有点炫酷有点diao的免费wordpress模板主题

这是一款经典的免费wordpress主题&#xff0c;被广泛应用于多个行业的网站。 https://www.wpniu.com/themes/189.html

Vue 监听器:让你的应用实时响应变化

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Python 一步一步教你用pyglet制作汉诺塔游戏

目录 汉诺塔游戏 1. 抓取颜色 2. 绘制圆盘 3. 九层汉塔 4. 绘制塔架 5. 叠加圆盘 6. 游戏框架 汉诺塔游戏 汉诺塔&#xff08;Tower of Hanoi&#xff09;&#xff0c;是一个源于印度古老传说的益智玩具。这个传说讲述了大梵天创造世界的时候&#xff0c;他做了三根金刚…