看了编程珠玑第八章中的内容,后面的课后练习也是非常的有意思,有一题是这么说的,给定整数m和整数n和实数向量x[n],
请找出使总和x[i]+……+x[i+m]最接近0的整数i。
我这里先不说明怎么获取最接近0的i,我先说明如何获得与0最接近的子数组之和。
看了写网上有些人已经多出了一些解法,但是觉得它们的做法好像都有些问题。例如这位大神的做法就是有些问题的。
int approximate(int * pArry, int len)
{
int * cum = 0;
int * realarry = new int[len + 1];
realarry[0] = 0;
cum = realarry + 1; //cum[-1] = 0
//累计pArry[0....i]的和存放于cum[i]中
for (int i = 0; i < len; i++)
{
cum[i] = cum[i - 1] + pArry[i];
}
sort(cum, cum + len); //对cum排序
int iMin = cum[1] - cum[0];
for (int k = 1; k < len; k++)
{
iMin = min(iMin, cum[k] - cum[k - 1]); //返回相邻两个元素差值最小的
}
return iMin;
}
式子中的cum[i]代表的是从第一个元素到该元素的累加和, 如果cum[l-1] = cum[n],那么cum[l..n]就是最接近0子向量,这里一定是0.但是,最接近0的子向量也很差不多,只要把所有的cum排序就是了,delta最小的就是了,整个算法复杂度就是排序了。他的代码中没有考虑cum[i] 直接为0的情况,显然这时候的意思是从数组的开始到该元素的累加和就是0。
他的代码对于这个example是会出错的[-1,-1,2,1,2]
其实只需要添加一行代码就可以了。
int approximate(int * pArry, int len)
{
int * cum = 0;
int * realarry = new int[len + 1];
realarry[0] = 0;
cum = realarry + 1; //cum[-1] = 0
//累计pArry[0....i]的和存放于cum[i]中
for (int i = 0; i < len; i++)
{
cum[i] = cum[i - 1] + pArry[i];
if(cum[i]==0)
return 0;
}
sort(cum, cum + len); //对cum排序
int iMin = cum[1] - cum[0];
for (int k = 1; k < len; k++)
{
iMin = min(iMin, cum[k] - cum[k - 1]); //返回相邻两个元素差值最小的
}
return iMin;
}