leetcode303. 区域和检索 - 数组不可变(easy)
题目描述:
给定一个整数数组 nums,求出数组从索引 i 到 j(i ≤ j)范围内元素的总和,包含 i、j 两点。
实现 NumArray 类:
- NumArray(int[] nums) 使用数组 nums 初始化对象
- int sumRange(int i, int j) 返回数组 nums 从索引 i 到 j(i ≤ j)范围内元素的总和,包含 i、j 两点(也就是 sum(nums[i], nums[i + 1], ... , nums[j]))
代码模版(swift)
class NumArray {
init(_ nums: [Int]) {
}
func sumRange(_ i: Int, _ j: Int) -> Int {
}
}
解法1(直接求和)
直观来看,我们可以维护一个数组存储输入的数组,每次计算范围和时遍历求和即可
class NumArray {
var numArr: [Int]!
init(_ nums: [Int]) {
numArr = nums
}
func sumRange(_ i: Int, _ j: Int) -> Int {
var sum = 0
for x in i...j {
sum += numArr[x]
}
return sum
}
}
-
空间复杂度:O(1) 未使用额外的存储空间
-
时间复杂度:初始化O(1),区间求和O(N*n),N表示求和次数,n表示数组长度,每次求和均需要一趟线性遍历
运行结果:
运行耗时2916ms,与其他swift代码相比明显耗时多了不少,考虑优化代码以降低时间复杂度
解法2(前缀和)
解法1直接求和为何执行时间长,分析时间复杂度后推知耗时操作在区间求和操作上,多次区间求和导致运行时间变长,是否可以考虑将耗时操作转移到代码初始化中?
思路:
用一个数组sumArr记录每个数字从0相加到对应到当前下标的和,比如sumArr[4]表示0-4的和,则指定i,j区间区间和为sumArr[j] - sumArr[i - 1],考虑到i - 1可能会小于0,可以创建一个长度为N+1的数组,用sumArr[j + 1] - sumArr[i]表示区间。代码如下
class NumArray {
var sumArr: [Int]! //用于存储0——(当前下标-1)的和
init(_ nums: [Int]) {
sumArr = [Int](repeating: 0, count: nums.count + 1) //创建一个长度为N+1的数组
guard nums.count > 0 else {
return //空数组保护
}
for i in 1...nums.count {
sumArr[i] = sumArr[i - 1] + nums[i - 1] //保存0——(i-1)的和
}
}
func sumRange(_ i: Int, _ j: Int) -> Int {
guard sumArr.count > 1 else {
return 0 //空数组保护
}
return sumArr[j + 1] - sumArr[i]
}
}
为何可以用sumArr[j + 1] - sumArr[i]表示区间和?sumArr[j + 1]表示nums从0——j的和, sumArr[i]表示nums从0——(i-1)的和,两者相减去除了0——(i-1)的和,剩下的就是区间i——j的和
-
空间复杂度:O(n) 使用了数组长度空间保存数组和
-
时间复杂度:初始化O(n),初始时需要遍历一趟存储前缀和,区间求和O(1)
执行结果:
可得这是一种更好的解题方法。
目前的算法题主要是看中时间复杂度,算法执行耗时越少越好。而如今大部分机器内存相当充裕,基本都会考虑用空间换取时间的方式来优化执行时间。
结语
这是本人第一次在掘金发布文章,响应掘金三月活动。本人从去年7月份开始算法刷题以来,每天刷一道每日一题。目前题数在300左右。希望各位希望提升技术的同僚共勉。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看活动详情