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

算法设计与分析(十三)

Count of Range Sum

更多技术博客 http://vilins.top/

题目

Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.
Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i ≤ j), inclusive.

Note:
A naive algorithm of O(n2) is trivial. You MUST do better than that.

Example:

Input: nums = [-2,5,-1], lower = -2, upper = 2,
Output: 3
Explanation: The three ranges are : [0,0], [2,2], [0,2] and their respective sums are: -2, -1, 2.

分析

题目的意思是给出一个数组和一个上界和下界,要求在连续的区间范围求和,使得和在给出的下界和上界的范围内,要求给出满足条件的区间的个数。
首先分析这个问题,这里涉及到区间求和,对于这类题目,一般的套路是算一个O(n)复杂度的连续求和,自然,题目也说到,通过一般的方法可以在O(n*n)的复杂度求出来,但这样显然会超时。
这里我们采用分治算法来算,首先我们理解以下基本事实:
对于两个有序的数组,我们假设其为n1和n2,那么对于n1的一个数,如果我们在n2找到最后下标i1使得n2[i1]<n1[i]并且有最后下标i2使得n2[i2]>n1[i],那么在n2中比n1[i]大的个数为i2-i1。

这里我们采用分治算法用到上述思想,我们不断将整个数组划分成小的问题,在最后一个只有两个元素时开始返回,计算对左边数组的每一个元素,我们利用上述的思想,每处理完一个子问题的时候对其进行归并排序的合并,使得问题的左右两边总是有序的,这样一来不断往上返回,最后得到结果。注意一个问题就是左右两边总是有序的,并且我们注意到左边数组的下标总是小于右边数组的下标,那么得到的范围也必定是从小下标到大的下标。

源码

class Solution { public: void mergeSort(vector<long>& sum, int b, int m, int e) { int n1 = m - b ; int n2 = e - m ; vector<long> left(n1, 0); vector<long> right(n2, 0); for(int i = 0; i < n1; i++) { left[i] = sum[b + i]; } for(int i = 0; i < n2; i++) { right[i] = sum[m + i]; } int i = 0, j= 0; for(int k = b; k < e; k++) { if((left[i] > right[j])) { sum[k] = right[j]; j++; if(j == n2) { for(int q = i; q < n1; q++) { sum[k+1+q-i] = left[q]; } break; } } else { //cout << "k "<< k << endl; sum[k] = left[i]; i++; if(i == n1) { for(int q = j; q < n2; q++) { sum[k+1+q-j] = right[q]; } break; } } } } int merge(vector<long>& sum, int lower, int upper, int low_index, int high_index) { if(high_index - low_index <= 1) { return 0; } int mid = (low_index + high_index)/2; int m = mid, n = mid, count = 0; count = merge(sum, lower, upper, low_index, mid) + merge(sum, lower, upper, mid, high_index); for(int i = low_index; i < mid; i++) { while((sum[m] - sum[i] < lower)&&m < high_index) { m++; } while((sum[n] - sum[i] <= upper)&&n < high_index) { n++; } count += (n - m); } mergeSort(sum, low_index, mid, high_index); //inplace_merge(sum.begin()+low_index, sum.begin()+mid, sum.begin()+high_index); return count; } int countRangeSum(vector<int>& nums, int lower, int upper) { vector<long> sum(nums.size()+1, 0); for(int i = 0; i < nums.size(); i++) { sum[i+1] = sum[i] + nums[i]; } return merge(sum, lower, upper, 0, nums.size()+1); } };

复杂度分析

算法复杂度分析,我们这里是基于递归实现的,排序的时间复杂度为O(n),在求解的时候,递归求解复杂度为logn,加上排序的开销,总的复杂度应该就是O(nlogn),而空间复杂度是用在对数组进行依次求和的存储,复杂度为O(n)。在对左右两个数组合并我用自己实现的函数时,效果会差一些,而用c++标准库的函数的时候,也就是用inplace_merge这个函数,计算起来会快一点点,可能是某些细节的优化问题。

更多技术博客 http://vilins.top/

http://www.rkmt.cn/news/1445290.html

相关文章:

  • 物联网项目实战:从传感器到云端的全栈开发指南
  • 渗透测试手记:如何用Gobuster搭配自定义字典,精准挖出靶场里的‘隐藏关卡’
  • 别再只会用timeout了!Windows批处理(bat)的5个隐藏技巧:从窗口美化到模拟黑客屏保
  • 深度解析Awoo Installer:Nintendo Switch游戏安装器的架构设计与实现原理
  • 别再让GC卡顿你的游戏了!Unity性能优化实战:对象池、延迟GC与内存管理避坑指南
  • KMS智能激活工具:Windows和Office永久激活的终极完整指南
  • 从高频交易到Kaggle Grandmaster:跨领域思维如何塑造顶尖数据科学家
  • 告别环境配置噩梦:用VSCode+ESP-IDF插件5分钟搞定ESP32开发环境(Windows保姆级)
  • 极空间NAS用户专属:26元/年搞定Obsidian全平台同步(DDNSTO 4M带宽实测与配置详解)
  • 基于Arduino与PID控制的智能循线机器人全流程实现
  • 量子密钥分发中的时钟同步技术解析
  • 避开这些坑!STM32G070 IAP升级中Flash分区与向量表重映射的实战解析
  • 别再只用ReLU了!手把手教你用Python代码可视化SwiGLU,看LLaMA为啥选它
  • 如何快速打造个性化Obsidian笔记环境:Blue Topaz主题终极配置指南
  • 机器人长时程任务规划:从符号推理到空间接地的技术挑战与实践
  • CAJ转PDF的终极解决方案:caj2pdf-qt如何让格式壁垒成为历史?
  • 蛋白质组学检测中【抗体芯片】与【质谱检测】的差异解析
  • 3个技巧让Switch手柄秒变PC游戏神器:JoyCon-Driver开源项目深度解析
  • 告别封IP!用Python的curl_cffi库轻松绕过AKamai反爬(附韩亚航空实战代码)
  • 告别白屏花屏!LVGL移植到STM32时Heap/Stack设置、内存不足裁剪的实战指南
  • 别再只盯着WiFi了!LiFi在智能家居和工业4.0里的5个‘杀手级’应用场景
  • 全面掌握PyMobileDevice3:Python控制iOS设备的专业解决方案
  • 保姆级教程:用ESPFlashDownloadTool_v3.6.3给NodeMCU烧录固件,一次成功
  • 手把手教你用GitHub给Obsidian笔记做“时光机”:版本回退与多端同步一步到位
  • 基于Arduino与光敏电阻的光控窗帘系统设计与实现
  • UniRepLKNet的‘大核魔法’:从Dilated Reparam Block到多模态通用感知,一篇讲透设计精髓
  • Pixel手机WiFi图标老有感叹号?用ADB命令5分钟搞定(附小米/华为备用地址)
  • 写作压力小了!2026年必不可少的专业降AIGC工具
  • 避坑指南:STM32F407硬件IIC库函数调试,如何解决常见通信失败问题?
  • AI威胁论辨析:人类认知偏差与责任缺失才是真正风险源