LeetCode 981, 219, 78

目录

981. 基于时间的键值存储

题目链接

981. 基于时间的键值存储

标签

数组 二分查找 排序

思路

时间戳根据时间生成,时间越早,则时间戳越小;时间越迟,则时间戳越大。例如 由2000-01-01 00:00:00.000生成的时间戳 小于 由2001-01-01 00:00:00.000生成的时间戳。所以 按照时间先后顺序加入本数据结构的值是 按照时间戳 升序排列 的,所以可以考虑在查找时使用二分查找。

题目对本数据结构的实现作了提示:设计一个基于时间的键值数据结构,该结构可以在不同时间戳存储对应同一个键的多个值,并针对特定时间戳检索键对应的值。这就意味着要使用 Map 作为本数据结构的底层实现,key 为一个字符串,value 为这个字符串对应的 所有字符串及其时间戳组成的 集合。看似 value 也是 Map 类型,其实不然。由于这个集合是有序的,所以可以设计一个类 Pair 当作集合的元素类型,使用 List 来存储,Pair 类包含字符串 String value 及 其时间戳 int timestamp。所以最终存储数据的集合为 Map<String, List<Pair>>

对于 void set(String key, String value, int timestamp),直接获取 key 对应的链表 list,如果没有,则创建一个,然后将新的 pair 放入链表 list

对于 String get(String key, int timestamp),先获取 key 对应的链表 list,如果没有,则返回空字符串 ""。接下来就在链表 list 中使用二分查找查找指定的 timestamp

由于要找到 指定timestamp 或其之前的 最大timestamp 对应的 value,所以使用二分法的 前驱实现 ,如果对二分法的后继和前驱不熟悉,则可以看这篇文章:算法------二分法

代码

java 复制代码
public class TimeMap {
    // 对组,存放值的value和timestamp属性
    private static class Pair {
        String value;
        int timestamp;

        public Pair(String value, int timestamp) {
            this.value = value;
            this.timestamp = timestamp;
        }
    }

    private final Map<String, List<Pair>> map = new HashMap<>();

    public TimeMap() {}

    public void set(String key, String value, int timestamp) {
        // 获取key对应的list,如果没有,则新建一个ArrayList
        List<Pair> list = map.computeIfAbsent(key, k -> new ArrayList<>());
        list.add(new Pair(value, timestamp)); // 将新值放入key对应的list中
    }

    public String get(String key, int timestamp) {
        List<Pair> list = map.get(key); // 获取key对应的list
        if (list == null) { // 如果没有
            return ""; // 则返回空字符串
        }

        // 由于要找到 指定timestamp 或其之前的 最大timestamp 对应的value,所以使用二分法的前驱实现
        int left = 0, right = list.size() - 1;
        while (left < right) {
            int mid = left + (right - left + 1 >> 1);
            if (timestamp < list.get(mid).timestamp) {
                right = mid - 1;
            } else {
                left = mid;
            }
        }
        // 如果能找到比 指定timestamp 小的value,则返回它;否则返回空字符串
        return list.get(left).timestamp <= timestamp ? list.get(left).value : "";
    }
}

219. 存在重复元素 II

题目链接

219. 存在重复元素 II

标签

数组 哈希表 滑动窗口

思路

本题就是判断 长度为 k 的区间内是否有重复元素

判重 最常用的就是 哈希表 HashSet,每次添加新值前先判断是否存在这个值,如果存在,则满足题目要求,返回true;否则才添加这个值。在 Java 中,可以在 add() 之前先使用 contains() 判断是否存在新值。不过,add() 是有返回值的,即返回是否添加成功,如果添加失败,则是因为有重复值。为了减少API的调用次数,从而减少耗费的时间,不使用 if (contains()) + add() 的组合,而是使用 if (!add()) 的组合。

比较复杂的是 控制区间长度为 k :从头到尾顺序遍历数组,每遍历一个元素都将元素加入 HashSet,则当遍历到索引为 k + 1 的元素时,HashSet 中的元素个数为 k 个,此时就得先去除索引为 i - k - 1 的元素,然后才能将索引为 i 的元素加入 HashSet

代码

java 复制代码
class Solution {
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        Set<Integer> set = new HashSet<>(); // 存储数字的集合
        for (int i = 0; i < nums.length; i++) {
            if (i > k) { // 当遍历到索引为k + 1的元素后,集合的元素个数就等于k了
                set.remove(nums[i - k - 1]); // 每次都需要去除一个元素
            }
            if (!set.add(nums[i])) { // 如果nums[i]添加失败,则说明在集合中存在nums[i]
                return true; // 返回true
            }
        }
        return false;
    }
}

78. 子集

题目链接

78. 子集

标签

位运算 数组 回溯

思路

在高中数学中学到过这样的知识:对于一个 n n n 个元素的集合,它的子集共有 2 n 2^n 2n 个 。可以用 排列组合 的思想理解它:子集中的元素是从原集合中选取的,对于这 n n n 个元素中的每一个元素,都有 选取不选取 这 2 2 2 种行为,共需要选择 n n n 次,所以子集(含空集 ∅ \emptyset ∅,即不选择任何一个值;也含全集,即选择全部值)共有 2 n 2^n 2n 种情况。

本题就是对选取 n n n 个元素的模拟:

  • 判断是否对所有元素都做了选择,如果是,则将选取的元素作为一个集合放到结果链表中;否则进行下列操作。
  • 选取 这个元素,然后对下一个元素做选择。
  • 然后 不选取 这个元素,然后对下一个元素做选择。

对于使用什么数据结构存储选取的元素,由于需要选取和不选取,即先将某个元素加入集合,然后再把这个元素从集合中删除,这是典型的 先进后出 操作,所以使用 来存储选取的元素。

代码

java 复制代码
class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        res = new ArrayList<>((int) Math.pow(2, nums.length));
        dfs(nums, 0);
        return res;
    }

    private List<List<Integer>> res; // 存储结果的集合
    private final LinkedList<Integer> sub = new LinkedList<>(); // 存储子集元素的栈

    /**
     * 对nums进行深度优先搜索
     * @param nums 待遍历数组
     * @param curr 当前元素索引
     */
    private void dfs(int[] nums, int curr) {
        if (curr == nums.length) { // 如果将nums遍历完毕
            res.add(new ArrayList<>(sub)); // 将栈中的元素全部加入结果集合中
            return; // 然后返回
        }

        // 先选取这个元素,并遍历下一个元素
        sub.push(nums[curr]);
        dfs(nums, curr + 1);

        // 然后不选取这个元素,并遍历下一个元素
        sub.pop();
        dfs(nums, curr + 1);
    }
}
相关推荐
机器学习之心几秒前
一区北方苍鹰算法优化+创新改进Transformer!NGO-Transformer-LSTM多变量回归预测
算法·lstm·transformer·北方苍鹰算法优化·多变量回归预测·ngo-transformer
儿时可乖了3 分钟前
使用 Java 操作 SQLite 数据库
java·数据库·sqlite
ruleslol5 分钟前
java基础概念37:正则表达式2-爬虫
java
yyt_cdeyyds11 分钟前
FIFO和LRU算法实现操作系统中主存管理
算法
xmh-sxh-131421 分钟前
jdk各个版本介绍
java
daiyang123...33 分钟前
测试岗位应该学什么
数据结构
alphaTao37 分钟前
LeetCode 每日一题 2024/11/18-2024/11/24
算法·leetcode
天天扭码40 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶41 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺1 小时前
Spring Boot框架Starter组件整理
java·spring boot·后端