当前位置: 首页 > backend >正文

力扣热题——统计完全子数组的数目

目录

题目链接:2799. 统计完全子数组的数目 - 力扣(LeetCode)

题目描述

解法一:滑动窗口 + 哈希表

Java写法:

C++写法:

运行时间

时间复杂度和空间复杂度

时间复杂度

空间复杂度


题目链接:2799. 统计完全子数组的数目 - 力扣(LeetCode)

注:下述题目描述和示例均来自力扣

题目描述

给你一个由  整数组成的数组 nums 。

如果数组中的某个子数组满足下述条件,则称之为 完全子数组 :

  • 子数组中 不同 元素的数目等于整个数组不同元素的数目。

返回数组中 完全子数组 的数目。

子数组 是数组中的一个连续非空序列。

示例 1:

输入:nums = [1,3,1,2,2]
输出:4
解释:完全子数组有:[1,3,1,2]、[1,3,1,2,2]、[3,1,2] 和 [3,1,2,2] 。

示例 2:

输入:nums = [5,5,5,5]
输出:10
解释:数组仅由整数 5 组成,所以任意子数组都满足完全子数组的条件。子数组的总数为 10 。

提示:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 2000

解法一:滑动窗口 + 哈希表

  1. 滑动窗口 + 哈希表

    • 使用滑动窗口来枚举所有可能的子数组。
    • 使用哈希表(或数组)来记录当前窗口内的不同元素及其出现次数。
    • 动态调整窗口大小,确保窗口内的不同元素数目等于整个数组的不同元素数目。
  2. 优化计算

    • 先计算出整个数组的不同元素数目 totalDistinct
    • 滑动窗口的过程中,一旦窗口内的不同元素数目等于 totalDistinct,就可以快速统计以当前右边界为终点的所有合法子数组。
  3. 复杂度分析

    • 时间复杂度:O(n),其中 n 是数组长度。每个元素最多被左右指针访问两次。
    • 空间复杂度:O(k),其中 k 是数组中不同元素的最大数目(题目限制 k ≤ 2000)。

Java写法:

import java.util.HashMap;public class Solution {public int countCompleteSubarrays(int[] nums) {// Step 1: 计算整个数组的不同元素数目 totalDistinctint totalDistinct = countDistinct(nums);// Step 2: 使用滑动窗口统计完全子数组的数目int n = nums.length;HashMap<Integer, Integer> freqMap = new HashMap<>();int left = 0, right = 0;int result = 0;while (right < n) {// 扩展窗口,将 nums[right] 加入窗口freqMap.put(nums[right], freqMap.getOrDefault(nums[right], 0) + 1);// 收缩窗口,直到窗口内的不同元素数目等于 totalDistinctwhile (freqMap.size() == totalDistinct) {// 统计以当前 right 为右边界的所有合法子数组result += n - right;// 移除左边界元素freqMap.put(nums[left], freqMap.get(nums[left]) - 1);if (freqMap.get(nums[left]) == 0) {freqMap.remove(nums[left]);}left++;}// 移动右边界right++;}return result;}// 辅助函数:计算数组中的不同元素数目private int countDistinct(int[] nums) {boolean[] seen = new boolean[2001]; // 根据题目限制,nums[i] <= 2000int distinctCount = 0;for (int num : nums) {if (!seen[num]) {seen[num] = true;distinctCount++;}}return distinctCount;}// 测试用例public static void main(String[] args) {Solution solution = new Solution();int[] nums1 = {1, 3, 1, 2, 2};System.out.println(solution.countCompleteSubarrays(nums1)); // 输出: 4int[] nums2 = {5, 5, 5, 5};System.out.println(solution.countCompleteSubarrays(nums2)); // 输出: 10}
}

C++写法:

#include <iostream>
#include <unordered_map>
#include <vector>using namespace std;class Solution {
public:int countCompleteSubarrays(vector<int>& nums) {// Step 1: 计算整个数组的不同元素数目 totalDistinctint totalDistinct = countDistinct(nums);// Step 2: 使用滑动窗口统计完全子数组的数目int n = nums.size();unordered_map<int, int> freqMap;int left = 0, right = 0;int result = 0;while (right < n) {// 扩展窗口,将 nums[right] 加入窗口freqMap[nums[right]]++;// 收缩窗口,直到窗口内的不同元素数目等于 totalDistinctwhile (freqMap.size() == totalDistinct) {// 统计以当前 right 为右边界的所有合法子数组result += n - right;// 移除左边界元素freqMap[nums[left]]--;if (freqMap[nums[left]] == 0) {freqMap.erase(nums[left]);}left++;}// 移动右边界right++;}return result;}private:// 辅助函数:计算数组中的不同元素数目int countDistinct(const vector<int>& nums) {vector<bool> seen(2001, false); // 根据题目限制,nums[i] <= 2000int distinctCount = 0;for (int num : nums) {if (!seen[num]) {seen[num] = true;distinctCount++;}}return distinctCount;}
};// 测试用例
int main() {Solution solution;vector<int> nums1 = {1, 3, 1, 2, 2};cout << solution.countCompleteSubarrays(nums1) << endl; // 输出: 4vector<int> nums2 = {5, 5, 5, 5};cout << solution.countCompleteSubarrays(nums2) << endl; // 输出: 10return 0;
}

运行时间

时间复杂度和空间复杂度

时间复杂度

  1. 计算不同元素数量 countDistinct

    • 这个过程需要遍历整个数组一次,因此时间复杂度是 O(n),其中 n 是数组的长度。
  2. 滑动窗口主循环

    • 主循环中,右指针 right 从数组的开始到结束进行遍历,即最多遍历 n 次。
    • 在最坏情况下,左指针 left 也会遍历整个数组。但是每个元素最多被左右指针各访问一次,所以这部分操作的时间复杂度也是 O(n)。
    • 因此,整个滑动窗口部分的时间复杂度为 O(n)。
  3. 综合考虑

    • 计算不同元素数量加上滑动窗口的过程,总的时间复杂度是 O(n) + O(n) = O(n)。

空间复杂度

  1. 存储不同元素的状态 seen

    • 使用了一个大小为 2001 的布尔数组(根据题目限制 nums[i] <= 2000),用于标记每个数字是否出现过。空间复杂度为 O(k),其中 k 是数组中不同元素的最大数目,在这里 k 最大为 2000。
  2. 哈希表 freqMap

    • 哈希表用于记录当前窗口内每个元素的频率。在最坏情况下,哈希表可能包含所有不同的元素。因此,空间复杂度也是 O(k),k 同上。
  3. 其他变量

    • 其他使用的额外空间(如整数变量)相对于输入规模来说是常数级别的,不影响总体的空间复杂度分析。

http://www.xdnf.cn/news/2422.html

相关文章:

  • 【MQ篇】RabbitMQ之死信交换机!
  • Node.js CSRF 保护指南:示例及启用方法
  • react slot传递
  • Python 操作 Excel 插入图表:解锁数据可视化的高效密码
  • 使用vue2 开发一个纯静态的校园二手交易平台-前端项目练习
  • WEBSTORM前端 —— 第2章:CSS —— 第3节:背景属性与显示模式
  • SpringMVC 通过ajax 前后端数据交互
  • 空间矩阵的思考
  • SpringMVC框架
  • 二、Web服务常用的I/O操作
  • HTML5 新特性详解:语义化标签、表单与音视频嵌入
  • pytorch写张量pt文件,libtorch读张量pt文件
  • 网络基础概念
  • HCIP知识点总结思维导图
  • Redis远程链接应用案例
  • 【计算机网络物理层】从信号传输到介质选型的核心技术解析
  • Web服务器技术选型指南:主流方案、核心对比与策略选择
  • 数据可视化 —— 饼图
  • 《MySQL 技术内幕-innoDB 存储引擎》笔记
  • 简述删除一个Pod流程?
  • HTTP:十二.HTTPS
  • UE 新建一个自带光照的场景
  • Git常用命令简明教程
  • 【每日随笔】文化属性 ① ( 天机 | 强势文化与弱势文化 | 文化属性的形成与改变 | 强势文化 具备的特点 )
  • 有源晶振输出匹配电阻选择与作用详解
  • AUTOSAR_Feature_Model_Analysis
  • 近期有哪些断链危机?如何提升供应链风险管理能力?
  • 头歌实训之游标触发器
  • 【Jupyter 启动时如何指定目录】
  • Android开机动画资源包制作(测试使用)