题目地址:
https://www.lintcode.com/problem/subarray-sum-closest/description
给定一个数组,求其中的和最接近 0 0 0的子区间的左右端点。
思路是,先求前缀和,然后对前缀和排序,接着只需扫描一遍前缀和数组,看看相邻两数的差的最小值即可。这里由于要返回端点下标,所以需要额外记录前缀和的下标。举例:对于数组
(
−
3
,
1
,
1
,
−
3
,
5
)
(-3,1,1,-3,5)
(−3,1,1,−3,5)
先计算其前缀和:
(
0
,
−
3
,
−
2
,
−
1
,
−
4
,
1
)
(0,-3,-2,-1,-4,1)
(0,−3,−2,−1,−4,1)需要注意的是,对于长度为
n
n
n的数组,其前缀和数组长度也是
n
n
n,这样方便于计算
[
i
,
j
]
[i,j]
[i,j]区间内的数字和,即为
s
[
j
+
1
]
−
s
[
i
]
s[j+1]-s[i]
s[j+1]−s[i]。接下来对其排序:
4
1
2
3
0
5
−
4
−
3
−
2
−
1
0
1
\begin{matrix} 4 & 1 & 2 & 3 & 0 & 5 \\ -4 & -3 & -2 & -1 & 0 & 1 \\ \end{matrix}
4−41−32−23−10051其中第二行是前缀和数组的值,第一行是每个值在原数组中的下标。我们发现前缀和数组相邻数字的差的最小值是
1
1
1,所以只需要挑出差为
1
1
1的前缀和数组的下标还原成原数组下标即可。注意,由于这里是和最接近
0
0
0的子区间,所以在前缀和做差的时候,即使下标先后次序是倒着的也没问题,真正的区间和只不过是反了号而已。比如我们看到
s
[
1
]
−
s
[
4
]
=
1
s[1]-s[4]=1
s[1]−s[4]=1,这意味着
s
[
4
]
−
s
[
1
]
=
a
[
1
]
+
a
[
2
]
+
a
[
3
]
=
−
1
s[4]-s[1]=a[1]+a[2]+a[3]=-1
s[4]−s[1]=a[1]+a[2]+a[3]=−1,虽然原区间并不存在和等于
1
1
1的子区间,但变了符号以后离
0
0
0的距离是相等的。如果要求一个和距离一个非零数的最近的子区间,上面的方法是无效的。
import java.util.Arrays;
public class Solution {
// 新开一个类,一个field记录前缀和,另一个field记录下标
class Pair {
int sum, index;
public Pair(int sum, int index) {
this.sum = sum;
this.index = index;
}
}
/*
* @param nums: A list of integers
* @return: A list of integers includes the index of the first number and the index of the last number
*/
public int[] subarraySumClosest(int[] nums) {
// write your code here
Pair[] pairs = new Pair[nums.length + 1];
// 初始化第一个值
pairs[0] = new Pair(0, 0);
// 计算前缀和并赋值
for (int i = 0; i < nums.length; i++) {
pairs[i + 1] = new Pair(pairs[i].sum + nums[i], i + 1);
}
// 按照前缀和来排序
Arrays.sort(pairs, (p1, p2) -> p1.sum < p2.sum ? -1 : 1);
int[] res = new int[2];
int cur = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) {
int tmp = pairs[i + 1].sum - pairs[i].sum;
// 如果找到了更小的差,就更新
if (tmp < cur) {
cur = tmp;
// 接下来要找到区间的左右端点
int begin = pairs[i + 1].index, end = pairs[i].index;
if (begin > end) {
int tmp = begin;
begin = end;
end = tmp;
}
// 更新最终结果
res[0] = begin;
res[1] = end - 1;
}
}
return res;
}
}
时间复杂度 O ( n log n ) O(n\log n) O(nlogn),空间 O ( n ) O(n) O(n)。