题目:给定一个子数组,求子数组和最接近0
分析:
方法1:
这个问题和求子数组最大值优点相似,但解法不同,如果按照求子数组最大值的方法来求解,我们可以求出以j为截止的最大值和最小值,如果最大值和最小值都>0,那么最小值即为所求,如果都<0,那么最大值就为所求,如果有一个值为0,那么为0的就为所求,但问题是如果最大值>0而最小值<0的情况,需要向前回溯才能解决。因此复杂度还是N^2。
方法2:
我们先计算所有sum[0-j] 0<= j <n,然后对sum[0-j]的数组进行排序,那么对于任何i,j段的和等于:
sum[i-j]= sum[0-j] - sum[0-i];
因为已经对sum[0-j]进行了排序,所以只要找相邻的比较就可以了,找出绝对值最接近0的sum[0-j]和sum[0-i],那么i+1 到j 即是所求。
需要特别注意的一点是,sum的最前面要增加一个初值sum[-1-0] = 0,否则,如果开始的位置从0开始时,就会出现错误,编写程序时,开始就犯了这个错误。
具体程序如下:
#include <stdio.h>
template<typename T>
void Swap(T& a, T& b) {
T tmp = a;
a = b;
b = tmp;
}
template<typename T>
int Partition(T array[], int start, int end) {
int j = start - 1;
T last = array[end];
for (int i = start; i < end; ++i) {
if (array[i] < last) {
Swap(array[i], array[j+1]);
j++;
}
}
Swap(array[end], array[j+1]);
return j+1;
}
template<typename T>
void QuickSort(T array[], int start, int end) {
if (start < end) {
int pivot = Partition(array, start, end);
QuickSort(array, start, pivot - 1);
QuickSort(array, pivot + 1, end);
}
}
int Abs(int a) {
return a < 0 ? -1 * a : a;
}
class SumFromStart {
public:
SumFromStart() : end_(0), sum_(0) {}
int end_;
int sum_;
bool operator<(const SumFromStart& right) {
return sum_ < right.sum_;
}
};
bool SubArraySumNearZero(int array[], int length, int* start, int* end) {
if (length < 1) {
return false;
}
if (length == 1) {
*start = 0;
*end = 0;
return true;
}
SumFromStart sum_from_start[length + 1];
sum_from_start[0].sum_ = 0;
sum_from_start[0].end_ = -1;
for (int i = 1; i <= length; ++i) {
sum_from_start[i].sum_ = sum_from_start[i-1].sum_ + array[i-1];
sum_from_start[i].end_ = i - 1;
}
QuickSort(sum_from_start, 0, length + 1);
for (int i = 0; i <= length; ++i) {
printf("%d ", sum_from_start[i].sum_);
}
printf("\n");
int min = sum_from_start[1].sum_ - sum_from_start[0].sum_;
for (int i = 2; i < length; ++i) {
if (Abs(sum_from_start[i].sum_ - sum_from_start[i-1].sum_) < min) {
if (sum_from_start[i].end_ < sum_from_start[i-1].end_) {
*start = sum_from_start[i].end_ + 1;
*end = sum_from_start[i-1].end_;
} else {
*start = sum_from_start[i-1].end_ + 1;
*end = sum_from_start[i].end_;
}
}
}
return true;
}
int main(int argc, char** argv) {
int array[] = {-2, 3, 4, -5, 8, 2, -2};
int start = -1;
int end = -1;
SubArraySumNearZero(array, sizeof(array) / sizeof(int), &start, &end);
printf("%d %d\n", start, end);
}
参考文献: