按照原题的意思是在题目上进行扩展。
本来是要找一个连续子数组,其和最大。
那么现在习题的要求是找到这么一个连续子数组,其和的绝对值最接近0。
O(n^3)或O(^2)的实现
都很好处理。O(n^3)到O(n^2)的差别在于可以一次性求出sum(i ~~j)的值。这个可以想一下的。
如下例
int sum[n];
sum[0] = a[0];
for (int i = 1; i < n; ++i)
sum[i] = sum[i-1] + a[i];
然后
return sum[j] - sum[i] + a[i];则可
O(nlogn)的方法
这里需要指出的是有一种O(nlogn)的方法,方法与前面类似。就是先求出sum[0 ~~i]保存在sum[i];中。
然后找到sum[j] 与sum[i]绝对值最小的那个数。
在这里,题意就转变成了求一个数组中绝对值最小的两个数。
一个数组中绝对值最小的两个数
先排序,然后比较相邻的两个数,选择出绝对值最小的两个则可。是不是很简单?
习题答案
这里给出第8章习题答案吧。
主体函数是
long long absToZero(int *a, int n, int *b, int *e)
返回值是最小绝对值,b, e分别表示开头与结束。
#include
#include
#include
#define abs(x) ((x) > 0 ? (x) : 0 - (x))
#define swap(a, b) ({int t = a; a = b; b = t;})
// O(nlogn) implementation
typedef struct _node
{
long long value;
int idx;
}node;
int cmp(const void *a, const void *b)
{
return (*(node*)a).value - (*(node*)b).value;
}
long long absToZero(int *a, int n, int *b, int *e)
{
int i = 0, idx_a, idx_b;
long long s = (long long)(2147483646), v = 0, t = 0;
node *temp = NULL;
*b = *e = -1;
if (!a || n <= 0) return 0;
temp = (node*)malloc(sizeof(node) *(n+1));
if (NULL == temp){
fprintf(stderr, "Memory malloc error in fun: absToZero()\n");
exit(1);
}
for (i = 0; i < n; ++i){
temp[i].value = (t += a[i]);
temp[i].idx = i;
v = abs(t);
if (v < s){
s = v; idx_a = 0; idx_b = i;
}
}
qsort(temp, n, sizeof(node), cmp);
s = abs(s);
for (i = 0; i < n - 1; ++i){
v = temp[i].value - temp[i+1].value;
v = abs(v);
if (v < s){
s = v; idx_a = temp[i].idx; idx_b = temp[i+1].idx;
}
}
free(temp); temp = NULL;
if (idx_a > idx_b) swap(idx_a, idx_b);
*b = idx_a; *e = idx_b;
return s;
}
// O(n^2) implementation
long long _sum(int *a, int b, int e)
{
int i = 0;
long long s = 0;
for (i = b; i <= e; ++i){
s += a[i];
}
s = abs(s);
return s;
}
long long O2abs(int *a, int n, int *b, int *e)
{
long long s = _sum(a, 0, n - 1);
long long v = 0;
int i, j;
for (i = 0; i < n; ++i){
for (j = i; j < n; ++j){
v = _sum(a, i , j);
if (v < s){ s = v; *b = i; *e = j;}
}
}
return s;
}
int main(void)
{
int n, *a = NULL, i = 0, j = 0, v, b, e, from, to, wrong = 0;
long long rea, reb;
for (i = 0; i < 1000 & 0 == wrong; ++i){
n = rand() % 100 + 1;
a = (int*) malloc(sizeof(int) *(n+1));
if (NULL == a){
fprintf(stderr, "Memory malloc error in main()\n");
exit(1);
}
for (j = 0; j < n; ++j){
v = rand() % 100;
if (rand() & 1) v = 0 - v;
a[j] = v;
}
if ((rea = O2abs(a, n, &b, &e)) != (reb = absToZero(a, n, &from, &to))){
printf("[%lld %d %d], [%lld %d %d]\n", rea, b, e, reb, from, to);
wrong = 1;
for (j = 0; j < n; ++j)
printf("%d ", a[j]);
printf("\n");
}
free(a); a = NULL;
}
return 0;
}
不知道是否有动态规划的方法可以降低复杂度啊?有没有O(n)的算法呢?
还有就是第二个思考题,如果要找最接近t的值呢。如何处理?想想吧。