按照原题的意思是在题目上进行扩展。
本来是要找一个连续子数组,其和最大。
那么现在习题的要求是找到这么一个连续子数组,其和的绝对值最接近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<stdio.h> #include<stdlib.h> #include<string.h> #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的值呢。如何处理?想想吧。