2024年4月26日力扣每日一题(1146)

2024年4月26日力扣每日一题(1146)

前言

​ 这道题在做的时候感觉很简单,题意很容易理解,但直接去做直接干爆内存,参考了一下灵神的代码,豁然开朗,觉得这道题很有意思,便想着写篇博客记录一下,自己在算法上的欠缺还很多,如有不足或错误之处还望见谅。

1.快照数组

在这里插入图片描述

​ 首先阅读一下题目,题目要求我们实现一个接口,每调用一次get方法,会更新一下索引index的数据,每调用一次get方法,会返回根据snap_id的数据来返回index的历史数据。

2.思路

​ 看到这道题,我首先想到的就是暴力计算,不停的复制数组,但这种方法,大概率不会通过,不是超时就是爆内存,在最坏的情况下,这道题需要复制50000次长度为50000的数组,这会超出内存限制,这让我头大,没了思路,想到map集合可以根据键值对存储数据,似乎可以用map来实现,但具体该怎么使用,自己没有一个思路,这时候去看了题解,瞻仰了一下灵神的代码题解,感觉豁然开朗,大佬就是大佬,太强了。

3.灵神的代码

class SnapshotArray {
    // 当前快照编号,初始值为 0
    private int curSnapId;

    // 每个 index 的历史修改记录
    private final Map<Integer, List<int[]>> history = new HashMap<>();

    public SnapshotArray(int length) {
    }

    public void set(int index, int val) {
        history.computeIfAbsent(index, k -> new ArrayList<>()).add(new int[]{curSnapId, val});
    }

    public int snap() {
        return curSnapId++;
    }

    public int get(int index, int snapId) {
        if (!history.containsKey(index)) {
            return 0;
        }
        List<int[]> h = history.get(index);
        int j = search(h, snapId);
        return j < 0 ? 0 : h.get(j)[1];
    }

    // 返回最大的下标 i,满足 h[i][0] <= x
    // 如果不存在则返回 -1
    private int search(List<int[]> h, int x) {
        // 开区间 (left, right)
        int left = -1;
        int right = h.size();
        while (left + 1 < right) { // 区间不为空
            // 循环不变量:
            // h[left][0] <= x
            // h[right][1] > x
            int mid = left + (right - left) / 2;
            if (h.get(mid)[0] <= x) {
                left = mid; // 区间缩小为 (mid, right)
            } else {
                right = mid; // 区间缩小为 (left, mid)
            }
        }
        // 根据循环不变量,此时 h[left][0] <= x 且 h[left+1][0] = h[right][0] > x
        // 所以 left 是最大的满足 h[left][0] <= x 的下标
        // 如果不存在,则 left 为其初始值 -1
        return left;
    }
}

作者:灵茶山艾府
链接:https://leetcode.cn/problems/snapshot-array/solutions/2756291/ji-lu-xiu-gai-li-shi-ha-xi-biao-er-fen-c-b1sh/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

4.自己对这道题的理解

1.computeIfAbsent方法

​ 在看灵神代码时出现了一个陌生的API,看来自己的知识欠缺还比较多,去查了一下这个API的使用方法,简单介绍一下。

computeIfAbsent 是 Java 8 引入的一个非常有用的 Map 接口方法,它允许你根据指定的键来条件性地计算并插入一个值。如果指定的键尚未与值关联(或者映射到 null),则它会使用提供的函数计算一个值,并将其与该键关联。

这是其基本用法:

Map<K, V> map = ...; // 假设你已经有了一个Map  
  
V value = map.computeIfAbsent(key, k -> {  
    // 在这里,你可以根据键 k 来计算一个值  
    // 例如,你可能想从数据库或其他数据源获取数据  
    return computeValue(k);  
});

在这个例子中,key 是你要检查的键,而 k -> computeValue(k) 是一个 Function,它根据键 k 来计算一个值。

  • 如果 map 中已经存在与 key 关联的值(即使该值为 null),那么 computeIfAbsent 将返回该值,并且不会执行提供的函数。
  • 如果 map 中不存在与 key 关联的值,那么 computeIfAbsent 将执行提供的函数,使用其返回的值与 key 关联,并返回该值。

这个方法非常有用,因为它允许你以原子方式执行“如果键不存在则插入”的操作,而无需首先检查键是否存在。这可以简化代码,并减少并发环境中可能发生的竞态条件。

注意:computeIfAbsent 不会替换已经存在的值(即使该值为 null)。如果你需要替换已经存在的值(包括 null),你应该使用 computemerge 方法。

那么在灵神的代码中

 public void set(int index, int val) {
        history.computeIfAbsent(index, k -> new ArrayList<>()).add(new int[]{curSnapId, val});
    }

​ 这段代码的意思是,如果键k,没有对应的值,那就新建一个列表,并且在这个列表中,添加一个数组,这个数组的数据包含两个值,一个是当前的快照编号,另一个是当前要设置的值。

2.解题思路

​ 根据灵神的解题思路,每次修改之后,不再重复去复制数组,而是单纯再后面添加一个元素,并且由于每次修改后添加的数据是有序的,在调用get方法时,我们可以使用二分查找来查找相对应的历史版本。

​ 二分查找

​ 这道题的二分查找也是一个比较值得注意的点,自己在写的时候,简单的二分查找难住了我不短的时间。

private int search(List<int[]> l, int x) {  
    int left = 0;  
    int right = l.size() - 1;  
    int result = -1; // 初始化result为-1,表示尚未找到匹配项  
  
    while (left <= right) {  
        int mid = left + (right - left) / 2;  
  
        if (l.get(mid)[0] <= x) {  
            // 如果中间位置的快照ID小于等于x,更新result并继续在右侧搜索,以找到可能的更大快照ID  
            result = mid;  
            left = mid + 1;  
        } else {  
            // 如果中间位置的快照ID大于x,则在左侧继续搜索  
            right = mid - 1;  
        }  
    }  
  
    // 返回不大于x的最大快照ID的索引,如果没有找到则返回-1  
    return result;  
}

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

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

相关文章

【YOLO改进】换遍IoU损失函数之GIoU Loss(基于MMYOLO)

GIoU损失函数 论文链接:https://arxiv.org/pdf/1902.09630 GIoU&#xff08;Generalized Intersection over Union&#xff09;损失函数是一种用于改善目标检测模型中边界框回归的方法。它是基于传统的IoU&#xff08;交并比&#xff09;损失的一个改进版本&#xff0c;解决了…

node.js的安装与配置

Node.js 是一种基于 JavaScript 编程语言的服务器端平台&#xff0c;它可以让你在浏览器之外运行 JavaScript 代码。以下是 Node.js 的安装步骤&#xff1a; 下载 Node.js&#xff1a; 访问 Node.js官网。根据你的操作系统选择合适的版本下载。 运行安装文件&#xff1a; 在下载…

计算机视觉——使用OpenCV GrabCut算法从图像中移除背景

GrabCut算法 GrabCut算法是一种用于图像前景提取的技术&#xff0c;由Carsten Rother、Vladimir Kolmogorov和Andrew Blake三位来自英国剑桥微软研究院的研究人员共同开发。该技术的核心目标是在用户进行最少交互操作的情况下&#xff0c;自动从图像中分割出前景对象。 在Gra…

直流有刷电机入门

文章目录 123455.25.3 1 2 电刷 材质是 石墨 3 130马达 就几毛钱 几块钱这学的就是减速电机P MAX一定 pf*v 降低速度 扭矩就会大 4 还有空载电流 过大负载 时 有堵转电流 &#xff08;可分析电流 来看电机工作状态&#xff09;RPM 转每分钟 5 5.2 这的线圈 是简化后的转子绕组…

Ubuntu终端常用指令

cat cat 读取文件的内容 1、ls 一、 1、ll 显示当前目录下文件的详细信息,包括读写权限,文件大小,文件生成日期等(若想按照更改的时间先后排序,则需加-t参数,按时间降序(最新修改的时间排在最前)执行: $ ll -t, 按时间升序执行: $ ll -t | tac): ll 2、查看当前所处路径(完整…

服务器数据恢复—服务器重装系统导致XFS分区丢失的数据恢复案例

服务器数据恢复环境&#xff1a; 一台服务器MD1200磁盘柜&#xff0c;通过raid卡将15块磁盘组建成一组raid5磁盘阵列。raid5阵列分配了2个lun&#xff0c;操作系统层面对lun进行分区&#xff1a;1个分区采用LVM扩容方式加入到了root_lv中&#xff0c;其余分区格式化为XFS文件系…

大数据时代,保护个人隐私小Tips Get 起来!

随着大数据时代的到来&#xff0c;我们的隐私正处于越来越易被侵犯的风险中。在各种社交媒体和信息共享平台上&#xff0c;我们需要输入各种个人信息&#xff0c;而这些信息可能被不法分子盗取&#xff0c;甚至被用来进行欺诈行为。在如今的大数据时代&#xff0c;保护个人隐私…

元宇宙中的DAPP:你了解多少?

元宇宙是什么&#xff1f;这是一个在当今科技圈炙手可热的话题。而在元宇宙中&#xff0c;DAPP起着至关重要的角色&#xff0c;它作为连接现实世界与虚拟世界的桥梁&#xff0c;为未来的数字世界开启了一个全新的篇章。 一、元宇宙&#xff1a;一个虚拟的数字世界 元宇宙是一…

【JavaWeb】Day51.Mybatis动态SQL(一)

什么是动态SQL 在页面原型中&#xff0c;列表上方的条件是动态的&#xff0c;是可以不传递的&#xff0c;也可以只传递其中的1个或者2个或者全部。 而在我们刚才编写的SQL语句中&#xff0c;我们会看到&#xff0c;我们将三个条件直接写死了。 如果页面只传递了参数姓名name 字…

【麒麟(Linux)系统远程连接到windows系统并进行文件传输】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言使用步骤总结 前言 一般来说&#xff0c;windows自带远程桌面&#xff0c;使用的RDP协议&#xff0c;Linux上支持RDP协议的软件很多&#xff0c;常用的是Remmi…

Java 网络编程之TCP(五):分析服务端注册OP_WRITE写数据的各种场景(二)

接上文 二、注册OP_WRITE写数据 服务端代码&#xff1a; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.S…

【cf】Codeforces Round 941(Div.2)题解 A - D

前三题出的最快的一次&#xff0c;但是d没出 A. Card Exchange 只要有一种颜色大于等于 k&#xff0c;那就是 k-1&#xff0c;否则就是 n #include <bits/stdc.h>using namespace std;#define int long long using i64 long long;typedef pair<int, int> PII;…

CONSOB 又下令封锁5个未经授权的投资网站,总数达1065

FX110讯&#xff1a;意大利金融市场监管局 CONSOB 已下令关闭 5 个非法提供金融服务/金融产品的网站。自2019年7月CONSOB有权下令封锁欺诈性金融网站以来&#xff0c;被封禁的网站数量已升至1065个。 以下是 CONSOB 下令新屏蔽的 5个网站&#xff1a; “Luno Invest” Vantage …

C#基础:WPF中常见控件的布局基础

一、用ViewBox实现放缩控件不变 二、布局代码 <Window x:Class"WpfApp1.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"Title"MainWindow"…

将静态资源解析成组件使用的库

vite版本的vite-plugin-svgr vite-plugin-svgr - npm 使用

排序试题解析(二)

8.4.3 01.在以下排序算法中&#xff0c;每次从未排序的记录中选取最小关键字的记录&#xff0c;加入已排序记录的 末尾&#xff0c;该排序算法是( A ). A.简单选择排序 B.冒泡排序 C.堆排序 D.直接插入排序 02&#xff0e;简单选择排序算法的比较次数和移动次数分别为( C )。…

MongoDB的安装(Linux环境)

登录到Linux服务器执行 lsb_release -a &#xff0c;即可得知服务器的版本信息为&#xff1a;CentOS 7。 # CentOS安装lsb_release包 [rootlinux100 ~]# sudo yum install redhat-lsb# 查看Linux版本 [rootlinux100 ~]# lsb_release -a LSB Version: :core-4.1-amd64:core-…

Signed的本质和作用

前言 Verilog中的signed是一个很多人用不好&#xff0c;或者说不太愿意用的一个语法。因为不熟悉它的机制&#xff0c;所以经常会导致运算结果莫名奇妙地出错。其实了解了signed以后&#xff0c;很多时候用起来还是挺方便的。 signed的使用方法主要有两种&#xff0c;其中一种…

【C语言】动态内存分配(一)

目录 1.为什么要有动态内存分配 2.malloc和free 2.1malloc 2.2free 1.为什么要有动态内存分配 我们已经掌握的内存开辟方式有: 但是上述的开辟空间的方式有两个特点: ⭐空间开辟大小是固定的。 ⭐数组在申明的时候&#xff0c;必须指定数组的长度&#xff0c;数组空间一旦…

网络安全与密码学

一、密码学概述 一、 密码学是一门研究信息安全保密的学科&#xff0c;主要涉及对信息进行加密、解密以及相关的安全技术和理论。 它通过使用各种加密算法和技术&#xff0c;将明文信息转换为密文&#xff0c;以确保信息在传输和存储过程中的保密性、完整性和真实性。密码学在…
最新文章